Skip to content

__attribute__((__interrupt__)) doesn't produce correct code #26

Closed
@cocoacrumbs

Description

@cocoacrumbs

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. The exx instruction is generated.
  • The routine ends with a ret instruction while this should be a reti 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 a ret instruction left that probably shouldn't be there?

Best regards,

Koen

0004-Fix-EI-RETI_partial-fix.patch.zip

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingmiscompileIncorrect code was generated

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions