CPCWiki forum

General Category => Programming => Topic started by: cpcitor on 10:57, 05 January 15

Title: Sharing feedback on using SDCC for a non-trivial C program.
Post by: cpcitor on 10:57, 05 January 15
Hello,

Here is a short report about SDCC and cpc-dev-toolchain based on recent days of work using it for an actual project (a small color and grid-based game).

SDCC can be used in many styles of C vs ASM mixtures. This reports is based on thinking clean C (for its obvious benefits when writing non-trivial algorithms) while staying aware of the CPC limitations.

I have written C, run code, checked generated ASM and here's the result.

Short summary: in my experience SDCC generates always robust and often pretty short and fast assembly code, perhaps thanks to the help of some C-level wisdom.

Variable types.

To put it short, use relevant types, by default unsigned integers, smallest needed size, e.g. :


#include <stdint.h>

int main() {
  uint8_t myvar;
}


Apply all C-level wisdom! Make const everything that is!

It's just even more important than on other platforms.  Make const everything that is ! SDCC will optimize very well, replacing e.g. heavyweight generic multiplication by on-the-spot optimized code, replacing a static table separately initialized with whatever is best suited in an optimized code.


#include <stdint.h>

const char mystring[]="foo";
const uint8_t mytable[] = { 0xde, 0xad, 0xbe, 0xef };

int myfunction(const uint8_t myparameterchar, const uint16_t myparameter16bit, const unit8_t* mypointer)
{
/* ... */
}

int main() {
  uint8_t *const my_const_pointer_to_mutable_variable;
  const uint8_t *my_mutable_pointer_to_const_variable;
  const uint8_t *const my_const_pointer_to_const_variable;
}


The compiler will then nag you when you write bad code.  Just following his advice will allow it to propagate the knowledge of what can be optimized, boost your code speed, keep its memory footprint small and your C-level design clean.

One exception: static const should be always better (or at least as well) optimized than const.  SDCC 3.4.0 generates less good code with static const than with const.  See for example c - Const vs Static Const - Stack Overflow (http://stackoverflow.com/questions/6381088/const-vs-static-const) . This should be discussed with SDCC authors.

Beware of non-const static variables

Non-const static variables won't be initialized properly if you don't have a crt0.s that takes care.  So far I did not need a crt0.s, because all my static variables were actually constant.

For an illustration with CPC-side screenshots, see this particular message Reply #43 on: 01:03, 27 October 13 in thread: cpc-dev-tool-chain: a portable toolchain for C/ASM development targetting CPC. (http://www.cpcwiki.eu/forum/programming/cpc-dev-tool-chain-a-portable-toolchain-for-casm-development-targetting-cpc/msg70053/#msg70053)


Struct: very good support. Not fully ANSI but that's fine.

Struct are good. Eat some more. C source can be made very clean and readable. Generated assembly code is clean and efficient.

SDCC is not fully ANSI as it refuses a few valid operations like copy struct by assignment, use struct in pass-by-value function parameter or as return value (remember they are copied through the stack, danger). No real problem actually, though memcpy(&a, &b, sizeof(a)); or even a macro is less elegant than a=b; -- not needed so far, even in test code.

Good use of local variables

Using local variables has two effects:
(1) putting them on the stack (limited to 384 bytes on the CPC unless you don't use the firmware stack but your own)
(2) at an address not known at compile time (contrary to global variables)

Because of (1) it's easy to write valid C code that overflows the CPC stack on the spot (for example declare as local variable an array taking 1kb or so and write values in it).  Once you are aware and avoid this trap, unless you use recursion heavily you should be safe.  (I might add some lightweight protection later, for example compile-time check in cpc-dev-tool-chain: a portable toolchain for C/ASM development targetting CPC. (http://www.cpcwiki.eu/forum/programming/cpc-dev-tool-chain-a-portable-toolchain-for-casm-development-targetting-cpc/) .)

Because of (2) code will be less efficient than plain code that reads and write at fixed address.

Moreover, on SDCC local variables are not optimized for heavy use. Generated code is perfect when a function declares a handful of local integer or pointers, but inefficient when declaring an array with a handful of initialized values at the same time (it writes all values, even those not explicitly set, by repeating the code to write one value) -- this happened to me only in test code anyway. You can work around this by declaring first then setting manually a few values separately.

Where to wisely optimize

Unless you write a demo maximizing the number of palette change by scan line, optimizing comes last.  I write simple, lean and clean everywhere, making unhampered development.  Only after, if proven necessary, comes optimization, with data structures and algorithm first, then C-level (e.g. replace extremely short functions with macros), then only if needed ASM-level (not needed so far in current project).

All in all, a minimal set of deviations from a clean design will provide most of the benefit with minimal effort.  (ref Program optimization - Wikipedia, the free encyclopedia (http://en.wikipedia.org/wiki/Program_optimization) ).  Wolfenstein and Doom were written fully in C with a handful of ASM routines like "draw a vertical line" http://ftp://ftp.idsoftware.com/idstuff/source/wolfsrc.zip (http://ftp://ftp.idsoftware.com/idstuff/source/wolfsrc.zip) .

My simple game holds the game state in a "big" struct.  Initially it was declared a local variable in main().

The only change I did is declare the big struct as global variable instead. If fixed (1) above.

I still continue to pass a pointer to it on every function. This does not solve (2) but that has no practical impact.  Code is much shorter anyway that what I've seen on z88dk.  Most time is spent on the display, not on reading values anyway, so I optimized the display routine instead (C-level optimization so far is enough, not a single line of assembly).

When I develop the AI it will probably need to "simulate" some game states and so I will need to call these functions with other game instances anyway.

Overall judgment

SDCC allows for a solid development environment, provided the programmer is aware of what actually happens when programming in C, e.g. what actually happens when declaring a local variable, a global variable, a static variable.

Enjoy and happy new year !
Title: Re: Sharing feedback on using SDCC for a non-trivial C program.
Post by: Xifos on 19:54, 05 January 15
Hello,

I'm a sdcc user on cpc (winape).
Your post is very interesting.
:)
Title: Re: Sharing feedback on using SDCC for a non-trivial C program.
Post by: cpcitor on 21:45, 05 January 15
Quote from: Xifos on 19:54, 05 January 15
Hello,

I'm a sdcc user on cpc (winape).
Your post is very interesting.
:)

I'm really happy you liked it. I enjoyed doing those experiments, though it took some time.

Even if I'm considering open-sourcing the project (it will be eventually) I should really incorporate that into the dev tool chain.
Powered by SMFPacks Menu Editor Mod