Description
Hi,
I tried using __attribute__((__interrupt__))
so that a C routine is called when an interrupt happens but I discovered the following:
- There is a missing
ex af, af'
instruction at the beginning and end of the interrupt routine. Theexx
instruction is generated. - The routine ends with a
ret
instruction while this should be areti
instruction. - Interrupt handling is not re-enabled because there is no
ei
instruction at the end of the interrupt routine. - Offsets used in e.g.
ld (ix - nn), rr
instructions are wrong.
Consider the following C code:
__attribute__((__interrupt__))
void interrupt_code_generation_test(void)
{
int a;
int b;
int c;
a = 1;
b = 2;
c = a + b;
} /* end interrupt_code_generation_test */
void non_interrupt_code_generation_test(void)
{
int a;
int b;
int c;
a = 1;
b = 2;
c = a + b;
} /* end non_interrupt_code_generation_test */
int main(void)
{
int a;
int b;
int c;
a = 1;
b = 2;
c = a + b;
return c;
} /* end main */
I get the following assembly code back when using this command line ./ez80-none-elf-clang -S -target ez80-none-elf main.c
:
section .text,"ax",@progbits
assume adl = 1
section .text,"ax",@progbits
public _interrupt_code_generation_test
_interrupt_code_generation_test:
exx
push ix
ld ix, 0
add ix, sp
ld hl, -9
add hl, sp
ld sp, hl
ld hl, 1
ld de, 2
ld (ix - 9), hl
ld (ix - 12), de
ld hl, (ix - 9)
ld de, (ix - 12)
add hl, de
ld (ix - 15), hl
ld hl, 9
add hl, sp
ld sp, hl
pop ix
exx
ret
section .text,"ax",@progbits
section .text,"ax",@progbits
public _non_interrupt_code_generation_test
_non_interrupt_code_generation_test:
push ix
ld ix, 0
add ix, sp
ld hl, -9
add hl, sp
ld sp, hl
ld hl, 1
ld de, 2
ld (ix - 3), hl
ld (ix - 6), de
ld hl, (ix - 3)
ld de, (ix - 6)
add hl, de
ld (ix - 9), hl
ld hl, 9
add hl, sp
ld sp, hl
pop ix
ret
section .text,"ax",@progbits
section .text,"ax",@progbits
public _main
_main:
push ix
ld ix, 0
add ix, sp
ld hl, -12
add hl, sp
ld sp, hl
or a, a
sbc hl, hl
ld de, 1
ld bc, 2
ld (ix - 3), hl
ld (ix - 6), de
ld (ix - 9), bc
ld hl, (ix - 6)
ld de, (ix - 9)
add hl, de
ld (ix - 12), hl
ld hl, (ix - 12)
ld iy, 12
add iy, sp
ld sp, iy
pop ix
ret
section .text,"ax",@progbits
ident "clang version 14.0.0 (https://github.com/jacobly0/llvm-project.git b1303ec02a1932c74c306658339d3386d7f46b47)"
extern __Unwind_SjLj_Register
extern __Unwind_SjLj_Unregister
In the assembly code we can see that the offsets in _interrupt_code_generation_test
are different than those (correctly) generated for _non_interrupt_code_generation_test
.
We also see that for _interrupt_code_generation_test
, the ex af, af'
, reti
and ei
instructions are missing.
I was able to work around this with a small wrapper assembly code like this to implement the isr_uart0()
routine:
_isr_uart0_wrapper:
ex af, af'
exx
call _isr_uart0
ex af, af'
exx
ei
reti
with _isr_uart0
being a normal C function with this prototype void isr_uart0(void)
and without the __attribute__((__interrupt__))
of course. Implemented like this I got a working uart0
interrupt routine .
This is also the way how the ZDS II C compiler generates code for C functions preceded by the #pragma interrupt
. I.e. starting with an ex af, af'
and exx
and ending with ex af, af'
, exx
, ei
and reti
.
Attached you can find a patch which generates the assembly code as described above but it might need improvements (I'm a total newbie regarding debugging compilers).
I did not find what causes the wrong offsets when __attribute__((__interrupt__))
is used (that will take a lot more time to study for me).
A minor problem arises when optimization is used. E.g. using this command line: ./ez80-none-elf-clang -S -target ez80-none-elf main.c -O1
:
section .text,"ax",@progbits
assume adl = 1
section .text,"ax",@progbits
public _interrupt_code_generation_test
_interrupt_code_generation_test:
ret
section .text,"ax",@progbits
section .text,"ax",@progbits
public _non_interrupt_code_generation_test
_non_interrupt_code_generation_test:
ret
section .text,"ax",@progbits
section .text,"ax",@progbits
public _main
_main:
ld hl, 3
ret
section .text,"ax",@progbits
ident "clang version 14.0.0 (https://github.com/jacobly0/llvm-project.git b1303ec02a1932c74c306658339d3386d7f46b47)"
extern __Unwind_SjLj_Register
extern __Unwind_SjLj_Unregister
The problems I have here are:
- The interrupt code is optimized away. We can work around that by putting the interrupt handling code in its own C file and not using optimization for that file at the cost of a more CPU intensive interrupt handling maybe.
- For both
_interrupt_code_generation_test
and_non_interrupt_code_generation_test
there is still aret
instruction left that probably shouldn't be there?
Best regards,
Koen