News:

Printed Amstrad Addict magazine announced, check it out here!

Main Menu
avatar_cpcitor

Sharing feedback on using SDCC for a non-trivial C program.

Started by cpcitor, 10:57, 05 January 15

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

cpcitor

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 . 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.


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. .)

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 ).  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 .

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 !
Had a CPC since 1985, currently software dev professional, including embedded systems.

I made in 2013 the first CPC cross-dev environment that auto-installs C compiler and tools: cpc-dev-tool-chain: a portable toolchain for C/ASM development targetting CPC, later forked into CPCTelera.

Xifos

Hello,

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

cpcitor

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.
Had a CPC since 1985, currently software dev professional, including embedded systems.

I made in 2013 the first CPC cross-dev environment that auto-installs C compiler and tools: cpc-dev-tool-chain: a portable toolchain for C/ASM development targetting CPC, later forked into CPCTelera.

Powered by SMFPacks Menu Editor Mod