[sdcc] [cpctelera] Naked function assembly

Started by teopl, 13:13, 28 May 22

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

teopl

I am using cpctelera/sdcc to compile C code.

While debugging I have found out that my stack is corrupted (it is overwritten partially) but only if I use __naked for the function signature. (this function is interrupt handler function and is written in C mostly and inline asm partly)
 
Then I compared asm generated in both cases and I saw that this is problematic code segment:

if (!freeze_all) {

for (index = 0; index < NUM; ++index) {

if (interrupt_timeout[index]) {

if (interrupt_timeout[index] > _tick_step) {

interrupt_timeout[index] -= _tick_step;
}
else {

interrupt_timeout[index] = 0;

events_timeout[index] = 1;
}
}
}
}

When I don't use __naked (works fine - stack not overwritten)

[font=Verdana, Arial, Helvetica, sans-serif]LD IY, #5C45[/font]
LD A, (IY+#03)
OR (IY+#02)
OR (IY+#01)
OR (IY+#00)
JR NZ, #70C8
LD HL, #4F14
LD (HL), #00
LD IY, #4F14
LD L, (IY+#00)
LD H, #00
ADD HL, HL
LD BC, #4F0C
ADD HL, BC
EX (SP), HL
POP HL
PUSH HL
LD C, (HL)
INC HL
LD B, (HL)
LD A, B
OR C
JR Z, #70BA
LD IY, #4F17
LD L, (IY+#00)
LD H, #00
CP A
SBC HL, BC
JR NC, #70A5
LD E, (IY+#00)
LD D, #00
LD A, C
SUB E
LD C, A
LD A, B
SBC D
LD B, A
POP HL
PUSH HL
LD (HL), C
INC HL
LD (HL), B
JR #70BA
POP HL
PUSH HL
XOR A
LD (HL), A
INC HL
LD (HL), A
LD A, #53
LD HL, #4F14
ADD (HL)
LD C, A
LD A, #4E
ADC #00
LD B, A
LD A, #01
LD (BC), A
LD IY, #4F14
INC (IY+#00)
LD A, (IY+#00)
SUB #03
JR C, #706D

When I use __naked (stack overwritten)

[font=Verdana, Arial, Helvetica, sans-serif]LD IY, #5C45[/font]
LD A, (IY+#03)
OR (IY+#02)
OR (IY+#01)
OR (IY+#00)
JR NZ, #70CF
LD HL, #4F14
LD (HL), #00
LD IY, #4F14
LD L, (IY+#00)
LD H, #00
ADD HL, HL
LD BC, #4F0C
ADD HL, BC
LD (IX-#02),L
LD (IX-#01),H
LD C, (HL)
INC HL
LD B, (HL)
LD A, B
OR C
JR Z, #70C1
LD IY, #4F17
LD L, (IY+#00)
LD H, #00
CP A
SBC HL, BC
JR NC, #70A8
LD E, (IY+#00)
LD D, #00
LD A, C
SUB E
LD C, A
LD A, B
SBC D
LD B, A
LD L, (IX-#02)
LD H, (IX-#01)
LD (HL), C
INC HL
LD (HL), B
JR #70C1
LD L, (IX-#02)
LD H, (IX-#01)
XOR A
LD (HL), A
INC HL
LD (HL), A
LD A, #53
LD HL, #4F14
ADD (HL)
LD C, A
LD A, #4E
ADC #00
LD B, A
LD A, #01
LD (BC), A
LD IY, #4F14
INC (IY+#00)
LD A, (IY+#00)
SUB #03
JR C, #7069

When compared (left is with _naked):


I have read sdcc __naked manual and it didn't help (they do say that if I use C inside __naked function "there are many ways to shoot yourself in the foot" :) )

Any idea how to have __naked function and not crash the stack?

SpDizzy

#1
It's clear.

You are declaring your function as __naked, so the compiler will not generate any code for parameter passing and returning.

When you declare function as __naked, contents of function must be written on inline assembler, and your are responsible for saving any registers that need to be preserved, and add 'RET' instruction at the end of the function for returning.

And ugly and fast solution for that (taking into account that your interrupt handler function does not receive any parameter), is to just add the 'ret' statement at the very end of your interrupt handler function, like this:

__asm
ret
__endasm;


Hope it helps.

Anyway, you better try not to use assembly inside C functions, and better define them directly on assembly files.

teopl


QuoteYou are declaring your function as __naked, so the compiler will not generate any code for parameter passing and returning.

Like you already said interrupt function I am setting __naked to has no parameters and no return type also so this should not matter.


QuoteWhen you declare function as __naked, contents of function must be written on inline assembler, and your are responsible for saving any registers that need to be preserved, and add 'RET' instruction at the end of the function for returning.

This is the main question I guess - why I shouldn't use C inside __naked function (if you read documentation it's not forbidden, just not recommended)? My function works great with __naked + C (except for the for loop part which I disabled currently).

I already did push at function start:

    __asm;
        di
        push af
        push bc
        push de
        push hl
        push ix
        push iy
    __endasm;

and pop at function end: (of course ending with reti (same as ret as I see))

    __asm;
        pop  iy
        pop  ix
        pop  hl
        pop  de
        pop  bc
        pop  af
        ei
        reti
    __endasm;

Also, if you look at the image at the end of my first post, there is different assembler code generated in 2 cases.

I am wondering why is this?



teopl

I just fixed the issue by replacing access to array element from:

array[i]

to:


*((u8*)array + i)
I guess __naked somehow influences the compiler to use IX register to access array element and this register somehow is not good for this...

Anyway, it would be good to have some additional info about this topic (how to use __naked with cpctelera functions containing both asm and C)...

SpDizzy

#4
Sorry for late answering, I was at work.
Initially I didn't had apportunity to view your interrupt handler wrapper code, hence the response about preserving registers, stack,..and so on.
Your code seems right to me, I think main problem here is that SDCC uses 'IX' register as frame pointer for accesing local variables, and your first approach 'array[y]' also uses 'IX' as indirect addressing for storing 'hl' content on memory, overwritting that memory areas and making program crashing or undefined behaviour.
Your second approach is free of that problem as, I guess, does not need to store any value at the memory addresses pointed by 'IX +*'

Powered by SMFPacks Menu Editor Mod