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):
(https://i.ibb.co/2PFcTB6/Screenshot-2022-05-28-at-12-18-17.png) (https://ibb.co/NpFWMz3)
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?
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.
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?
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)...
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 +*'