Enlarge SDCC's stack

Pagina 2/4
1 | | 3 | 4

Van sp4

Master (214)

afbeelding van sp4

05-02-2015, 18:58

Marq wrote:

By looking at the crt0, it seems SDCC doesn't set its own stack. One thing to do is to avoid big local arrays or define them as static.

Is there any command of SDCC for enlarge the stack instede to define the variable as static?

Van Grauw

Ascended (10639)

afbeelding van Grauw

05-02-2015, 19:41

I looked at the manual but could not find such a setting. Actually, from what I was able to understand SDCC does not declare a fixed stack space (only on some other non-Z80 CPUs which require it), but just uses the existing stack which can grow to a very large size...

Also, your program looks so simple, I find it hard to believe that stack size is already a problem... That should only happen when you get many levels of recursion.

If I had to hazard a guess, your problem is not due to stack size, but e.g. caused by including the assembly code with the __naked annotation. Try to remove it...?

Van sp4

Master (214)

afbeelding van sp4

05-02-2015, 20:05

Grauw wrote:

I looked at the manual but could not find such a setting. Actually, from what I was able to understand SDCC does not declare a fixed stack space (only on some other non-Z80 CPUs which require it), but just uses the existing stack which can grow to a very large size...

Also, your program looks so simple, I find it hard to believe that stack size is already a problem... That should only happen when you get many levels of recursion.

If I had to hazard a guess, your problem is not due to stack size, but e.g. caused by including the assembly code with the __naked annotation. Try to remove it...?

I tried to remove the assembly code in the begin of play.c and that give me a lot of error message.
If the problem it was not in the stack, where is it?

Van Alcoholics_Anonymous

Resident (39)

afbeelding van Alcoholics_Anonymous

05-02-2015, 23:38

Can you provide a link to a zip file with the source inside? I think the forum is mangling some of it, making it difficult to scan.

The error you mentioned in the other thread can happen if your "play.h" does not include an "extern void play(...);" prototype and if "play.h" is not included in your app file.

The dangling inline asm you have at the beginning of the play file is definitely wrong. It will compile because sdcc will ignore the __asm stuff and pass that along to the assembler. The section it is assigned to will probably be correct because the C compiler will open with a .area declaration. But I have no idea how that code is called without a label attached to its beginning. Again, maybe the forum is obfuscating things.

sdcc touches nothing re: sp. If you need to move the stack, you have to do it yourself in main() or in the crt you provide. One thing you must know is that sdcc reserves ix for itself. If your code is changing it, it needs to save and restore ix. If you do not specify "--reserve-regs-iy" on the compile line, the same applies to iy.

Van hit9918

Prophet (2926)

afbeelding van hit9918

06-02-2015, 06:05

that "__naked" instruction means just "generate no code other than the inline asm".
like generate no code to set up IX register to acess variables.
then one has to get the parameters from the stack oneself.

Van Grauw

Ascended (10639)

afbeelding van Grauw

06-02-2015, 09:52

To counter the forum messing up source code, press the quote button and you’ll see the original text.

Re. the __naked annotation, from what I gathered from the SDCC manual (which was not Z80 specific), it also ensures all registers are preserved prior to calling the inline assembly code, whereas with __naked you need to preserve them yourself... Especially I wonder if it is ok to modify ix. It’s all not very well documented though, it doesn’t say explicitly which Z80 registers need to be preserved, and for the structure of the stack frame it refers to some old C compiler that I’ve never heard about and am wasn’t able to find more technical info about with a quick google either.

Van sp4

Master (214)

afbeelding van sp4

06-02-2015, 13:48

Alcoholics_Anonymous wrote:

Can you provide a link to a zip file with the source inside? I think the forum is mangling some of it, making it difficult to scan.

The error you mentioned in the other thread can happen if your "play.h" does not include an "extern void play(...);" prototype and if "play.h" is not included in your app file.

The dangling inline asm you have at the beginning of the play file is definitely wrong. It will compile because sdcc will ignore the __asm stuff and pass that along to the assembler. The section it is assigned to will probably be correct because the C compiler will open with a .area declaration. But I have no idea how that code is called without a label attached to its beginning. Again, maybe the forum is obfuscating things.

sdcc touches nothing re: sp. If you need to move the stack, you have to do it yourself in main() or in the crt you provide. One thing you must know is that sdcc reserves ix for itself. If your code is changing it, it needs to save and restore ix. If you do not specify "--reserve-regs-iy" on the compile line, the same applies to iy.

I tried to put in SDCC command "--reserve-regs-iy" and the compiler told me:

?ASlink-Warning-Undefined Global '_play' referenced by module 'app'

Nothing to do.
Where is the problem?

Van Alcoholics_Anonymous

Resident (39)

afbeelding van Alcoholics_Anonymous

06-02-2015, 16:58

sp4 wrote:

I tried to put in SDCC command "--reserve-regs-iy" and the compiler told me:

?ASlink-Warning-Undefined Global '_play' referenced by module 'app'

Nothing to do.
Where is the problem?

This problem is easy to fix.

Your header file:

void signal(char channel, int frequency, int volume);
void noise(char channel, int fnoise, char chnoiseen);
void play(unsigned char *stringA,  unsigned char *stringB, unsigned char *stringC);
....

must declare those functions "extern":

extern void signal(char channel, int frequency, int volume);
extern void noise(char channel, int fnoise, char chnoiseen);
extern void play(unsigned char *stringA,  unsigned char *stringB, unsigned char *stringC);

When you do not declare function prototypes "extern" you are telling the compiler that the function is defined in the current file -- that's the error you are seeing.

If you qualify prototypes as "extern" you are telling the compiler the function is declared somewhere out there in the global namespace (and maybe locally, so it's okay to include play.h with play.c).

The dangling __asm at the start of your "play.c" file:

/*asm_routine for manage the delays*/

__asm
{
HTIMI: EQU $FD9F 
VSYNC_ISR: EQU $F966

ld hl,HTIMI
ld de,VSYNC_ISR 

di 
ld (hl),$C3   	;FD9F: jp
inc hl        
ld (hl),e    	  ;FDA2: 66
inc hl 
ld (hl),d       ;FDA3: F9
inc hl

...
}

is not right. The assemblers C compilers use are more sophisticated than the ones normally used by z80 programmers. One of the features they use is areas/sections where code can be assigned to different memory regions. The C compiler uses it to separate code and data space among other things. This means every bit of code and data the C compiler generates has (for sdcc) ".area" directives embedded to place bits of asm code into the correct memory region. sdcc defines a few regions out of the box: "_CODE" - rom code, "_DATA" bss segment (variables that are zeroed at startup), "_INITIALIZER" / "_INITIALIZED" for vars that are non-zero at startup, etc.

So when the C compiler sees the function syntax: void foo(...) { ... } it knows this is stuff that's going into the _CODE segment. If you include just raw asm at the beginning of the file, it's hard to say what sdcc will do with it. For some predictability, if it's in a C file, you need to embed it as inline asm in a dummy __naked function. However the correct way to include pure asm in projects is to have them in separate .asm files and have everything linked together by the assembler (and remember your asm needs to be assigned to ".area _CODE"). I use sdcc via another compiler which does these things automatically so I can't say how to do it with sdcc on its own. You'd probably compile the C program to an object file and then use the assembler to assembler your asm files while linking to the object file. Anyway, this is mainly fyi to explain why you can't just make an __asm block appear on its own in c source.

The other gotcha is what I mentioned: sdcc expects you to preserve the value of ix. If you have any interrupts running, it must save ix. If you call your own custom asm functions, they must save ix. sdcc might use iy if you do not add "--reserve-regs-iy" to the compile line. It most often uses iy if you turn up the optimization (--max-allocs-per-node 200000, eg). If it uses iy it also expects you to preserve its value. Everything else is fair game.

Van sp4

Master (214)

afbeelding van sp4

06-02-2015, 17:04

Alcoholics_Anonymous wrote:

Can you provide a link to a zip file with the source inside? I think the forum is mangling some of it, making it difficult to scan.

Here you are, the play.c and play.h ---> http://creative75.altervista.org/creative/?p=10
You can download them at the begin where is wrote "Download play".

Van Alcoholics_Anonymous

Resident (39)

afbeelding van Alcoholics_Anonymous

06-02-2015, 17:15

Grauw wrote:

It’s all not very well documented though, it doesn’t say explicitly which Z80 registers need to be preserved, and for the structure of the stack frame it refers to some old C compiler that I’ve never heard about and am wasn’t able to find more technical info about with a quick google either.

ix and iy (when "--reserve-regs-iy" is not used) must be preserved. Everything else is ok to change.

sdcc uses one stack frame and that's the standard C right-to-left push order.

So:

int foo(int a, int b, int c)

will see this in the code that calls it:

...
push c
...
push b
....
push a
call _foo
pop af ; clear up stack
pop af
pop af

Inside _foo the stack frame is:

higher address
c (2 bytes)
b (2 bytes)
a (2 bytes)
return address (2 bytes)
lower address

with sp pointing at the LSB of the return address.

ints and pointers are pushed as 2 bytes. Longs are pushed as 4 bytes.

The difficult part is that chars are pushed as 1 byte. This can cause problems with vararg functions and is not ideal for functions implemented in asm. Currently I'm always declaring char function arguments using a 16-bit type for this reason.

For a function in C, chars taking up 1 byte on the stack is not a problem because sdcc is using ix to index to them. However, ix is a really inefficient way to access variables on the stack so functions implemented in asm are much better off popping the values from the stack and pops occur in 16-bit units.

An example (the __naked syntax might not be right as I don't do pure asm functions this way normally):

char __naked *strlen(char *s)
{
__asm

   pop af   ; return address
   pop hl   ; hl = char *s

   push hl  ; now restore stack
   push af

   ; enter: hl = char *s
   ;
   ; exit : hl = length
   ;        bc = -(length + 1)
   ;         a = 0
   ;        z flag set if 0 length
   ;        carry reset
   ;
   ; uses : af, bc, hl

   xor a
   ld c,a
   ld b,a
   
   cpir
   
   ld hl,$ffff
   sbc hl,bc

   ret

__endasm 
}  // and I think this might insert a 'ret' automatically

So always for asm functions, 16-bit arguments are best. The 8-bit size of chars on the stack for C functions also have a small cost at the point of the call because the 8-bit value must be placed in the MSB of a 16-bit register (usually "af"), then that has to be pushed on the stack and sp has to be adjusted upward by 1 (inc sp). So the caller also adds a few bytes to set up the call for this 1-byte char argument. The reason sdcc does this is because it also targets mpus like the 8051 with very small stack spaces where this is almost always desirable behaviour. On a z80, I think it's mostly desirable to use 16-bit parameters for chars. I don't know if there is a switch to get that but personally I am just declaring function args with int sizes rather than char sizes.

Pagina 2/4
1 | | 3 | 4