Author Topic: C programming using stdio on the CPC  (Read 661 times)

0 Members and 1 Guest are viewing this topic.

Offline nicf82

  • CPC464
  • **
  • Posts: 33
  • Country: gb
    • Carbon Internet
  • Liked: 8
  • Likes Given: 23
C programming using stdio on the CPC
« on: 17:00, 25 May 20 »
I'm trying to get to grips with some C programming on the CPC and need to so some basic IO to the screen like gets/puts/printf. I'm using CPCTelera which I realize is a games development framework but thought it should be ok for the basics too as it uses SDCC under the hood, although im happy to be corrected on that.

From what I can see CPCTelera implements the low-level function putchar which allows me to use puts/printf from stdio, but not getc so I cant use gets to read a complete string.


So after reading this page on the wiki I set about copying the CONIO.S listing into my project and creating a CONIO.H file to go with it, assuming that this would implement the low-level io methods correctly for the CPC. It all compiles fine with the CONIO.H included, but does not to allow me to use gets/fgets from stdio. So currently the best I can do to read a string from the keyboard is make direct repeated calls to cgetc which is defined in the CONIO.S.

Is there something I'm missing here or is what I am assuming incorrect, is it possible to use the methods from stdio on the CPC?

Cheers!

Nic

Offline pelrun

  • Supporter
  • 6128 Plus
  • *
  • Posts: 617
  • Country: au
    • index.php?action=treasury
  • Liked: 312
  • Likes Given: 189
Re: C programming using stdio on the CPC
« Reply #1 on: 17:34, 25 May 20 »
Edit: sorry, didn't quite read your post closely enough.
The conio.s doesn't hook into the stdlib at all, instead it defines it's own functions to do things. If you specifically want to use stdio, then you'll need to define your own getchar function. Which will actually not be much different to the _cgetc function defined in conio.s, so you might try just renaming it...
« Last Edit: 17:46, 25 May 20 by pelrun »

Offline nicf82

  • CPC464
  • **
  • Posts: 33
  • Country: gb
    • Carbon Internet
  • Liked: 8
  • Likes Given: 23
Re: C programming using stdio on the CPC
« Reply #2 on: 21:20, 25 May 20 »
Thanks for clarifying Pelrun!

I suppose the only other question I have is, is what i'm doing best practice when using C on th CPC? My only C knowledge has been with GNU glibc.Maybe things are just not as well defined for the CPC though?

Nic

Offline cpcitor

  • The user previously known as FindYWay
  • CPC6128
  • ****
  • Posts: 289
  • Country: fr
  • My heart still runs on traditional CPC.
    • My code for the CPC.
  • Liked: 142
  • Likes Given: 351
Re: C programming using stdio on the CPC
« Reply #3 on: 01:10, 26 May 20 »
Thanks for clarifying Pelrun!

I suppose the only other question I have is, is what i'm doing best practice when using C on th CPC? My only C knowledge has been with GNU glibc.Maybe things are just not as well defined for the CPC though?

Nic

Hi nicf82! I come from the same background as you, GNU etc.

Short version: cpc-dev-tool-chain has something, see below for demo.

SDCC provides function definitions, but no implementation

SDCC includes a ANSI header stdio.h which defines those:

Code: [Select]
extern int _print_format (pfn_outputchar pfn, void* pvoid, const char *format, va_list ap);

/*-----------------------------------------------------------------------*/
extern void printf_small (char *,...) _REENTRANT;
extern int printf (const char *,...);
extern int vprintf (const char *, va_list);
extern int sprintf (char *, const char *, ...);
extern int vsprintf (char *, const char *, va_list);
extern int puts(const char *);

extern int getchar(void);
extern int putchar(int);

SDCC lets each platform provide an implementation.

CPCTelera is heavily tuned for productions using 2D sprites and similar setup. I don't know if if provides implementations.

Anyway, cpc-dev-tool-chain provides something.

I wrote this plain source file in an empty directory named stdiotest :

Code: [Select]
#include <stdio.h>
#include <string.h>

void gets(char *);

void main()
{
    /* ---- 0123456789012345678901234567890123456789 */
    puts("Hello, world.\r\n\r\n"
           "This is cpc-dev-tool-chain's demo\r\n"
           "stdio/putchar/getchar/gets/puts/strlen.\r\n"
           "\r\n"
           "Anyway, please consider lightweight api\r\n"
           "targetting cpc firmware, like:\r\n"
           "* puts-like cfwi_txt_str0_output(),\r\n"
           "* getchar-like fw_km_wait_char(),\r\n"
           "* non-blocking fw_km_read_char()\r\n"
           "\r\n"
           "For doc, in cpclib/cfwi/include/cfwi/\r\n"
           "see fw_txt.h and fw_km.h\r\n"
           "in cpc-dev-tool-chain source tree.\r\n"
        );

    while (1)
    {
        puts("Please type some characters for gets.\r\n");
        char buffer[256];
        gets(buffer);
        printf("You typed %d characters: %s\r\n", strlen(buffer), buffer);

        puts("Let's getchar(). Press a key.\r");
        int char_entered;
        if (char_entered = getchar())
        {
            printf("You typed char: %c\r\n\r\n", char_entered);
        }
    }
}



I work with Linux. (cpc-dev-tool-chain can also run on Mac or Windows, where it needs more testing.)

From a local copy of https://github.com/cpcitor/cpc-dev-tool-chain I called

Code: [Select]
/pathtocdtc/cdtc-project-setup.sh /pathtomyproject/stdiotest
and the project was set up.

I typed make bin. First time fetches and compiles tools for a few minutes, next times are immediate.

It generated a 4k binary.

make dsk well, makes a dsk file.

make run runs the code inside cpcec emulator.

Here's a screenshot:

[attachimg=1]

The same program compiles on local Linux:

Code: [Select]
gcc main.c
./a.out

Hello, world.

This is cpc-dev-tool-chain's demo
stdio/putchar/getchar/gets/puts/strlen.

Anyway, please consider lightweight api
targetting cpc firmware, like:
* puts-like cfwi_txt_str0_output(),
* getchar-like fw_km_wait_char(),
* non-blocking fw_km_read_char()

For doc, in cpclib/cfwi/include/cfwi/
see fw_txt.h and fw_km.h
in cpc-dev-tool-chain source tree.

Please type some characters for gets.

Hello CPCWiki!
You typed 14 characters: Hello CPCWiki!
Let's getchar(). Press a key.
4
You typed char: 4

Please type some characters for gets.


What's next?

Well, as the demo says, please consider lightweight api targetting cpc firmware, like:
* puts-like cfwi_txt_str0_output(),
* getchar-like fw_km_wait_char(),
* non-blocking fw_km_read_char()

For doc, in cpclib/cfwi/include/cfwi/ see fw_txt.h and fw_km.h in cpc-dev-tool-chain source tree.

Why?

* cfwi_txt_str0_output is written specifically for the CPC in ASM not in C (shorter), does not add a linefeed.
* fw_km_wait_char() directly call firmware, tiny difference returns a byte not a 16-bit int.
* non-blocking fw_km_read_char() directly call firmware, allows your program to do something while waiting for you to press a key, without interrupt or (gasp) threads.
* you get much more control with CPC-specific control character, see cpc-dev-tool-chain/hello_world_using_sdcc/hello.c, and API, see https://github.com/cpcitor/cpc-dev-tool-chain/blob/master/cpclib/cfwi/include/cfwi/fw_txt.h


Had a CPC since 1985, currently software dev professional, including embedded systems.

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

Offline cpcitor

  • The user previously known as FindYWay
  • CPC6128
  • ****
  • Posts: 289
  • Country: fr
  • My heart still runs on traditional CPC.
    • My code for the CPC.
  • Liked: 142
  • Likes Given: 351
Re: C programming using stdio on the CPC
« Reply #4 on: 01:16, 26 May 20 »
Since you want to program in C for the CPC, you will probably be interested in playing with the source code of https://github.com/cpcitor/color-flood-for-amstrad-cpc .

This program is a kind-of canonical example of program written in portable C, using firmware every time it does a good enough job.

Only part where direct assembly is used is when drawing the colored grid.

Everything else, printing, waiting for keys, colors, redefined characters, arrows etc, is made with clean C code that calls cfwi functions which are a very thin wrapper to firmware functions, and sometimes no wrapper at all (the C function call directly translates to a Z80 CALL functionaddress). You cannot get any more lightweight than this!

Also, code is structured to isolate CPC-specific parts from platform-agnostic parts, thus getting a program that can run on local PC, although with very simple input and output... with plain stdio!
Had a CPC since 1985, currently software dev professional, including embedded systems.

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

Offline teopl

  • CPC664
  • ***
  • Posts: 111
  • Country: cs
  • Liked: 45
  • Likes Given: 95
Re: C programming using stdio on the CPC
« Reply #5 on: 01:40, 26 May 20 »
Thanks for clarifying Pelrun!

I suppose the only other question I have is, is what i'm doing best practice when using C on th CPC? My only C knowledge has been with GNU glibc.Maybe things are just not as well defined for the CPC though?

Nic

Using the C on CPC is very different from using it on PC mostly because of the (lack of) external libraries but also because of hardware limitations.

No console, no clock, ...

Few random things: (my experience with sdcc using cpctelera library for game development)

- you can't initialize global variable unless it's made const
- all variables must be declared in the block start (I forgot if that is also in regular C)
- know memory layout (if you use firmware, you can't disable it, where is stack, the screen memory...)
- memory size is very limited (maybe disable firmware? but then you can't use use it. use shared buffers, compress assets, ...)
- debugging is hard, I had quite a few crashes and memory/stack overwrites (use on screen, if in emulator like winape you can use also software breakpoints)
- use interrupt to do async tasks and timers
- if you want to do graphics learn how screen is refreshed (to avoid blinking wait vsync before drawing)
- to save memory reuse code, if accessing same memory multiple times - first store it in a variable (this also saves memory)
- there is no malloc :) only stack... (but you can make memory manager)
...

Personally I didn't want to rely on firmware or external headers except the ones from the cpctelera lib (it doesn't have sprintf etc...)

This gave possibility to disable firmware and have more room for code/data but then you need to do many things manually (which is also interesting)




Offline pelrun

  • Supporter
  • 6128 Plus
  • *
  • Posts: 617
  • Country: au
    • index.php?action=treasury
  • Liked: 312
  • Likes Given: 189
Re: C programming using stdio on the CPC
« Reply #6 on: 07:37, 26 May 20 »
Congratulations, and welcome to C programming for almost any embedded system  ;D
- you can't initialize global variable unless it's made const
- all variables must be declared in the block start (I forgot if that is also in regular C)
The first one I think is a bug in the crtc0.s.
The second is true for all C prior to C99. SDCC supports C99 and C11 you just have to enable it with an option.

Offline teopl

  • CPC664
  • ***
  • Posts: 111
  • Country: cs
  • Liked: 45
  • Likes Given: 95
Re: C programming using stdio on the CPC
« Reply #7 on: 09:47, 26 May 20 »
To correct myself:
you can't initialize global variable at the time of definition (of course you can set it later any time - if not const)

One old thread on the topic: https://www.cpcwiki.eu/forum/programming/cpctelerasdcc-initialization-of-variables-and-arrays-in-headerfile/msg112905/#msg112905

Offline pelrun

  • Supporter
  • 6128 Plus
  • *
  • Posts: 617
  • Country: au
    • index.php?action=treasury
  • Liked: 312
  • Likes Given: 189
Re: C programming using stdio on the CPC
« Reply #8 on: 10:03, 26 May 20 »
Yes, I know; that's generally what "initialisation" means in this context.
The reason stated in the thread you linked for not having the startup do the necessary copy from the data segment is hilarious. Supposedly it prevents "having two copies of the data", but it doesn't - that second copy still exists; just now it's the (less efficient) code you have to explicitly write to set the variable because the initialiser doesn't work.  :picard2: Hence why I said it's a bug in the crt0.s.

Offline nicf82

  • CPC464
  • **
  • Posts: 33
  • Country: gb
    • Carbon Internet
  • Liked: 8
  • Likes Given: 23
Re: C programming using stdio on the CPC
« Reply #9 on: 11:30, 26 May 20 »
Wow thanks everyone for the very helpful responses! I think the main gaps in my knowledge that I need to fill are around memory layout and memory management best practices which I'll investigate. Also how to use memory extensions, I have a DDI5 with 512k of memory but don't really understand how to even address that memory as of course we have 16-bit pointers?

Offline cpcitor

  • The user previously known as FindYWay
  • CPC6128
  • ****
  • Posts: 289
  • Country: fr
  • My heart still runs on traditional CPC.
    • My code for the CPC.
  • Liked: 142
  • Likes Given: 351
Re: C programming using stdio on the CPC
« Reply #10 on: 11:39, 26 May 20 »
memory layout

Complex topic. Simplest answer: if your C program starts at 0x4000 you maximize room for C and avoid any issue with firmware not able to access pointers to data below 0x4000 (because at the time the firmware runs, it is ROM in that area). The rest you can learn later.

This is why cpc-dev-tool-chain lets you choose, and defaults to 0x4000.

https://github.com/cpcitor/cpc-dev-tool-chain/blob/master/sdcc-project.Makefile#L19

You can change it in your project, see https://github.com/cpcitor/cpc-dev-tool-chain/blob/master/cdtc-project-setup.sh#L117

Also, cpc-dev-tool-chain asks SDCC to put variables right after the code. Again simplest thing to do? In most cases you won't need to change that.

memory management best practices which I'll investigate.

I'm preparing a longer answer.

Also how to use memory extensions, I have a DDI5 with 512k of memory but don't really understand how to even address that memory as of course we have 16-bit pointers?

They are called "far address". Have a look at the official firmware documentation. It's long but worth looking at here and there.
http://www.cpcwiki.eu/index.php/Soft968:_CPC_464/664/6128_Firmware
« Last Edit: 11:42, 26 May 20 by cpcitor »
Had a CPC since 1985, currently software dev professional, including embedded systems.

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

Offline cpcitor

  • The user previously known as FindYWay
  • CPC6128
  • ****
  • Posts: 289
  • Country: fr
  • My heart still runs on traditional CPC.
    • My code for the CPC.
  • Liked: 142
  • Likes Given: 351
Re: C programming using stdio on the CPC
« Reply #11 on: 12:15, 26 May 20 »
This answer is very long but hopefully packed full of interesting and insightful information. Please tell.

Using the C on CPC is very different from using it on PC...

Yes and no. Have a look at this code : color-flood-for-amstrad-cpc/model.c . It doesn't look much different from regular C, because it is regular C.

Especially function fillColorToProcessedState does a recursive flood fill, which looks kind of dangerous but works perfectly if you're aware of not crossing limitations.

...mostly because of the (lack of) external libraries but also because of hardware limitations.

Yes and no. The firmware provides a number of entry points which make things somehow comfortable. Look at Firmware coverage in cpc-dev-tool-chain&#039;s CFWI (C-firmware interface)
Firmware coverage in cpc-dev-tool-chain's CFWI (C-firmware interface)

No console,

Console? CPC console, yes.

See "generic" code drawing a window in https://github.com/cpcitor/color-flood-for-amstrad-cpc/blob/master/platform_sdcc/widget_impl.c#L26

For more, see tests in https://github.com/cpcitor/cpc-dev-tool-chain/tree/master/cpclib/cfwi/test/fw_txt

fw_txt* functions provide all the comfort you have in BASIC, better in some places (like hardware scrolling, even horizontal with void fw_scr_set_offset(uint16_t offset); ).

Code: [Select]
static const char const message[] = "\226\220\232\235 Amstrad CPC character set \227\232\220\234";

uint8_t perform_test( void )
{
        fw_scr_initialise();
        fw_scr_set_border( 2, 2 );

        fw_txt_set_pen( 3 );
        {
                uint8_t column = 20 - ( sizeof( message ) - 1 ) / 2;
                fw_txt_set_cursor( 2, column );
                cfwi_txt_str0_output( message );
        }
...


Code above yields very efficient asm:

Line uint8_t column = 20 - ( sizeof( message ) - 1 ) / 2; yields no code! Zero bytes! The compiler figures out it's constant.

Code: [Select]
                             64 ;testfixture.c:23: fw_txt_set_cursor( 2, column );
   4058 11 02 03      [ 3]   65 ld de, #0x0302
   405B D5            [ 4]   66 push de
   405C CD 43 41      [ 5]   67 call _fw_txt_set_cursor


no clock, ...

You have time in 1/300s resolution (if you don't kill your good friend the firmware that provides it to you).

uint32_t fw_kl_time_please(void);

Few random things: (my experience with sdcc using cpctelera library for game development)

As I wrote CPCtelera is heavily tuned to 2D sprite-based productions, and evolved from the very first early-experimental-release of cpc-dev-tool-chain.
So, most limitations in C on CPCtelera are CPCtelera's not the CPC.

you can't initialize global variable at the time of definition (of course you can set it later any time - if not const)

One old thread on the topic: https://www.cpcwiki.eu/forum/programming/cpctelerasdcc-initialization-of-variables-and-arrays-in-headerfile/msg112905/#msg112905

The topic is mostly right on the facts, yet things could be made clearer with some examples. Code and ASM above is a short example.

There is a test showing all the combinations offered by C, they are all supported by cpc-dev-tool-chain.
Comments in code show the bytes generated: https://github.com/cpcitor/cpc-dev-tool-chain/blob/master/tests/variable_storage_class_test/variable_storage_class_test.c

Congratulations, and welcome to C programming for almost any embedded system  ;D The first one I think is a bug in the crtc0.s.
The second is true for all C prior to C99. SDCC supports C99 and C11 you just have to enable it with an option.

pelrun is right.

On virtually all platforms where you can program in C, crt0.s is the traditional name for the link between "the operating system gave control to some ASM code supplied by the compiler + linker" and "call the main() function of the C program to run".

It is relatively simple. On bare-metal systems, it sets interrupts vectors, stack, possibly init hardware. Most platforms run code from some read-only area, so the compiled by default assumes this and makes provisions to copy them from "rom" to "ram".

On the CPC all setup is already done, crt0 becomes really simple. You can even do without it, but then you break the compiler's assumptions and get the bug.

Declaring variable const then accessing them through a non-const pointer is a hack. It works and is probably generated-code-efficient but is intellectually ugly and ugly in source code. I don't.

Anyway, after observing a few years ago that no crt0 for the cpc got it right, I wrote one, heavily commented. You can see it on
https://github.com/cpcitor/cpc-dev-tool-chain/blob/master/tests/variable_storage_class_test/crt0.s

cpc-dev-tool-chain will not force you to use it. Currently you have to copy it in your project. cpc-dev-tool-chain will pick it.

My rules of thumb regarding C-level best practices

* For integers, use shortest simplest types. I mostly uint8_t .
* make const anywhere you can. Even const char const* .
* Avoid global non-const variable. Use global const variables, they are fine. See below for how cpc-dev-tool-chain helps you.
* struct, typedef as much as you want example of complex struct with interdepent pointers
* #define kuux 41 is not typed and ugly. In some cases you don't have to use that. C says static const int foo=42; it not a compile-time constant but enum { bar = 43 }; is. You can typedef a struct depending on such int-typed value with clean syntax and full compiler check! See anonymous enum and typedef
* In some cases, in non-recursive functions, it makes sense to make some local variable static. This is because the Z80 is not very efficient at accessing a C-style stack frame relative to SP. sdcc copies SP to IX, still code is a little heavy. local static variables, especially if used heavily sometimes save more bytes than they consume by being allocated at all times.

When compiling with cpc-dev-tool-chain, you get a warning and a list of symbols that  you should check:
https://github.com/cpcitor/cpc-dev-tool-chain/blob/master/sdcc-project.Makefile#L293

Here's what it says on https://github.com/cpcitor/cpc-dev-tool-chain/blob/master/tests/variable_storage_class_test/variable_storage_class_test.c

WARNING: program uses global non-const variable initialized with compile-time constants, consuming 6 bytes twice.  It is good to do so, better than initializing them in code (main).  It is even better if the variables can be declared const: it will instantly divide memory consumption of those variables by 2.  Generally, declaring const whatever can be (including const pointer to const data) can yield shorter and faster code.  Please try to declare the symbols below const:
at 50D3 symbol __xinit__static_int_initialized
at 50D5 symbol __xinit__int_initialized
at 50D7 symbol __xinit__global_testinstance_initialized



- all variables must be declared in the block start (I forgot if that is also in regular C)

This limitation is C89. Recent C allow you that. SDCC even support most of C11! I do it all the time. It makes code much clearer and helps the compiler produce better code (along with other hints like const, static, etc). random example.

Also SDCC supports generics! I can't explain here (too long) but you can have some kind of comfort that you usually have only in C++, like print(variable) which automatically compiles correct code based on variable type.

- know memory layout (if you use firmware, you can't disable it, where is stack, the screen memory...)

(Like in my other answer)

Complex topic. Simplest answer: if your C program starts at 0x4000 you maximize room for C and avoid any issue with firmware not able to access pointers to data below 0x4000 (because at the time the firmware runs, it is ROM in that area). The rest you can learn later.

This is why cpc-dev-tool-chain lets you choose, and defaults to 0x4000.

https://github.com/cpcitor/cpc-dev-tool-chain/blob/master/sdcc-project.Makefile#L19

You can change it in your project, see https://github.com/cpcitor/cpc-dev-tool-chain/blob/master/cdtc-project-setup.sh#L117

Also, cpc-dev-tool-chain asks SDCC to put variables right after the code. Again simplest thing to do? In most cases you won't need to change that.

- memory size is very limited (maybe disable firmware? but then you can't use use it. use shared buffers, compress assets, ...)

Looks like you can politely ask the firmware to free much of its memory and even re-enable only the disc drive (and 1280 bytes it uses) on demand them reclaim them after. I'm still learning, see https://www.cpcwiki.eu/forum/programming/memory-limit-on-loading-programs/

- debugging is hard, I had quite a few crashes and memory/stack overwrites (use on screen, if in emulator like winape you can use also software breakpoints)

If you structure your program in platform-agnostic and platform-dependent parts, you'll have tthe comfort of your usual environment to debug the former.
Regarding the latter, make small steps, use versioning heavily (git with small independent commits) and come back one or more steps to isolate the faults.
Use cpcec integrated debugger.

- use interrupt to do async tasks and timers

Yes. I do that in my current project. Very nice.

- if you want to do graphics learn how screen is refreshed (to avoid blinking wait vsync before drawing)

Yes.

- to save memory reuse code, if accessing same memory multiple times - first store it in a variable (this also saves memory)

It may depend. I first use local variables with very local scope.

Best: after you compile, sdcc generates readable files:
.asm generated asm source
.lst generated asm source with bytes after assembly, without link-time addresses
.rst generated asm source with bytes after assembly, all as they are in the final linked binary
.map list of symbols and their adresses

I mostly look at the .rst, to check if SDCC generated efficient code. If not, it's generally because your source does not help the compiler in proving what can be simplified.

- there is no malloc :) only stack... (but you can make memory manager)
...

* SDCC provides malloc, with 1k buffer by default. It works out of the box but I never used it. In most programs for a CPC you don't need malloc anyway. There's not a single malloc in cpcitor/color-flood-for-amstrad-cpc.

Personally I didn't want to rely on firmware or external headers except the ones from the cpctelera lib (it doesn't have sprintf etc...)

This gave possibility to disable firmware and have more room for code/data but then you need to do many things manually (which is also interesting)

IMHO relying on firmware keeps things small and simple: simple code, simple interaction, lots of comfort.
color-flood is somehow complex algorithmically, yet works wonders.
« Last Edit: 17:01, 26 May 20 by cpcitor »
Had a CPC since 1985, currently software dev professional, including embedded systems.

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

Offline cpcitor

  • The user previously known as FindYWay
  • CPC6128
  • ****
  • Posts: 289
  • Country: fr
  • My heart still runs on traditional CPC.
    • My code for the CPC.
  • Liked: 142
  • Likes Given: 351
Re: C programming using stdio on the CPC
« Reply #12 on: 12:23, 26 May 20 »
Also, when you do "make bin", you can get query the memory map for interesting information.
For example:

Code: [Select]
grep ^_AFTERCODE *.map

_AFTERCODE                          0000A6EE    00000000 =           0. bytes (REL,CON)

This is the highest byte affected by your code and variables.

You can make a change in code (e.g. change variable to static, change type) and recompile. This will cause variations in generated code and you will see if this increase or decrease consumption.

Try using plain "int" then compare with "uint8_t".

Using this measuring device allowed me to shave 200 bytes out of a 1k-byte complex routine with lots of local variables. I took each in turn, changed it to static, and if it reduced consumption, kept it, else revert.

In practice, many 1 or 2 bytes variable were a win. Two variables were consuming 50 bytes each, and it was best to keep them local (non-static) so that they are allocated on the stack.

Remember, as soon as a function uses static variable where you intended a local, it becomes incapable of recursion/reentrancy. Most functions are okay, just think before.
« Last Edit: 12:27, 26 May 20 by cpcitor »
Had a CPC since 1985, currently software dev professional, including embedded systems.

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

Offline cpcitor

  • The user previously known as FindYWay
  • CPC6128
  • ****
  • Posts: 289
  • Country: fr
  • My heart still runs on traditional CPC.
    • My code for the CPC.
  • Liked: 142
  • Likes Given: 351
Re: C programming using stdio on the CPC
« Reply #13 on: 14:01, 26 May 20 »
Using the C on CPC is very different from using it on PC mostly because of the (lack of) external libraries but also because of hardware limitations.
Few random things: (my experience with sdcc using cpctelera library for game development)
(...)
Personally I didn't want to rely on firmware or external headers except the ones from the cpctelera lib (it doesn't have sprintf etc...)
This gave possibility to disable firmware and have more room for code/data but then you need to do many things manually (which is also interesting)

Thanks teopl for your testimony. Your experience is common and expected.

CPCtelera is strong on documentation, has an extensive library of well-optimized yet generic 2D routines, and and attracts much deserved attention.

What you describe is a world without cpc-dev-tool-chain. Yet it exists.

What cpc-dev-tool-chain is good for

Example: programs you would write in C on top of SDL or similar text-and-graphics-API-based C programming

Like you did in BASIC, but in C:

* text-based interaction, colors, BASIC-style windows,
* firmware graphics (lines, etc)
* algorithms.

In other words, cfwi, the C-firmware-interface of cpc-dev-tool-chain is kind of like SDL for the Amstrad CPC: write in simple C, get performance based on the actual platform API. Compare https://rawgit.com/cpcitor/cpc-dev-tool-chain/master/cpclib/cfwi/coverage.html with SDL documentation.

CPCtelera has some partial firmware coverage, cpc-dev-tool-chain is more advanced in this case.

Example: high-performance with dedicated routines

When needing graphical performance I considered using CPCtelera's routines, but in the end dedicated Z80-optimized routines that leverage exact context yield better performance than a generic (even Z80-optimized but generic) routine.

The current example is a game I'm working on, with tiles smoothly dropping from the top of the screen, covering nearly all the screen and animated at full framerate.

Integrate existing tools: cpc-dev-tool-chain is intended for full automatic workflow "build in one step" from source

Both CPCtelera and cpc-dev-tool-chain include a number of pre-existing tools.

cpc-dev-tool-chain calls the tools below as part of automated build: https://github.com/cpcitor/cpc-dev-tool-chain/tree/master/tool

* 2cdt
* addhead
* caprice32
* cpcec
* cpcxfs
* deexo
* exomizer
* gfx2crtc
* hex2bin
* idsk
* playtzx
* png2cpcsprite
* rasm
* sdcc

Also cpc-dev-tool-chain includes an original flexible tool to preprocess graphics for the CPC https://github.com/cpcitor/cpc-dev-tool-chain/tree/master/tool/png2cpcsprite


Full automatic workflow or "build in one step" means that you only ever have to manually handle files in their preferred editing state. If there is a chain of steps, all steps are handled automatically. You don't have to manually call a tool each time you update some kind of file. You don't commit or distribute half-processed files, like compressed files, graphics-converted-to-ASM-source, etc. (I've done it exceptionally because some files were generated by a closed-source tool and I don't want to require people to install this tool on their machine.) The default makefile handles simple common case. For the rest you have to write some local.Makefile with custom rules. I do this for graphics, to compress data to prepare a compressed loader.

CPCtelera includes some other tools with different dependencies. Some are closed-source, or at least distributed as binary, which sometimes creates obstacles.

To create a project: https://github.com/cpcitor/cpc-dev-tool-chain/blob/master/documentation/CDTC_with_custom_project.md
Once you have created a project, just run make and see a list of targets.

Experimental yet featureful

cpc-dev-tool-chain is more of an experimental tool yet it is more advanced on its strong points:

* very very lightweight (git checkout with complete history, before fetching external tools is about 5Mbytes, of which images are nearly 1Mbyte). cpc-dev-tool-chain fetches tools as needed. You don't have to download 100+Mbytes of tools you don't need.
* tests and demos (in cpclib/cfwi/test and tests subdirectories) and example https://github.com/cpcitor/color-flood-for-amstrad-cpc help you master C-level and asm-level details
* lots of small tools and tips, though not made very visible. You have to look at sdcc-project.makefile. It has grown messy with time, to be honest, yet shows many tricks under his sleeves.

In other words, playing with cpc-dev-tool-chain first may be a good first step.

Something that would be good is more documentation: some step-by-step howtos, make a more complete website with content generated from inline documentation. The essential documentation is there, just visit subdirectories in https://github.com/cpcitor/cpc-dev-tool-chain/tree/master/cpclib/cfwi/include/cfwi

Conclusion

People working on something which is technically a 2D game with sprites, where most effort is on graphics, storytelling, etc should go straight to CPCtelera.

People experienced with GNU tools and C but beginner about C on CPC should try cpc-dev-tool-chain. It looks like it's your case.

Many other cases (algorithms, BASIC-like programs etc) will benefit from the speed of C and the comfort of the firmware better with cpc-dev-tool-chain.

It's good that both exist.
« Last Edit: 16:56, 26 May 20 by cpcitor »
Had a CPC since 1985, currently software dev professional, including embedded systems.

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

Offline teopl

  • CPC664
  • ***
  • Posts: 111
  • Country: cs
  • Liked: 45
  • Likes Given: 95
Re: C programming using stdio on the CPC
« Reply #14 on: 11:08, 27 May 20 »
so basically it's a question of firmware or or not firmware :)

and also, do you make resource demanding game or not so demanding application/program...

aaaand if you want to port to other z80 machines, it is much easier if you don't use firmware.

anyway, I have not tried before cpc-dev-tool-chain, thanks @cpcitor for reminding me about that.

but I have problem with installation, the "make hello_world_using_sdcc" is OK, but the "make hello_world_using_sdcc-all" is stuck (I guess on wget).

do you have a quick tip for that? (I don't want to make large offtopic on this thread, just wanted to have a quick look)

PS I tried on Ubuntu 18.04

Offline cpcitor

  • The user previously known as FindYWay
  • CPC6128
  • ****
  • Posts: 289
  • Country: fr
  • My heart still runs on traditional CPC.
    • My code for the CPC.
  • Liked: 142
  • Likes Given: 351
Re: C programming using stdio on the CPC
« Reply #15 on: 11:42, 27 May 20 »
so basically it's a question of firmware or or not firmware :)

and also, do you make resource demanding game or not so demanding application/program...

aaaand if you want to port to other z80 machines, it is much easier if you don't use firmware.

anyway, I have not tried before cpc-dev-tool-chain, thanks @cpcitor for reminding me about that.

but I have problem with installation, the "make hello_world_using_sdcc" is OK, but the "make hello_world_using_sdcc-all" is stuck (I guess on wget).

PS I tried on Ubuntu 18.04

Thanks teopl. Here "make hello_world_using_sdcc-all" appears to work. I'll test again in an isolated environment.

do you have a quick tip for that?

Any copy-paste of terminal output may help.

do you have a quick tip for that? (I don't want to make large offtopic on this thread, just wanted to have a quick look)

Yes, you're right. The best place to discuss is open an issue on https://github.com/cpcitor/cpc-dev-tool-chain/issues

Cheers!
Had a CPC since 1985, currently software dev professional, including embedded systems.

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

Offline teopl

  • CPC664
  • ***
  • Posts: 111
  • Country: cs
  • Liked: 45
  • Likes Given: 95
Re: C programming using stdio on the CPC
« Reply #16 on: 12:57, 27 May 20 »
@cpcitor Not sure what happened, my guess is that my internet failed or I was not patient to wait for wget to finish (maybe it didn't show the progress).

Now the make finished and I check the helloworld, looks very nice!

Anyway, back to the topic :)

Offline cpcitor

  • The user previously known as FindYWay
  • CPC6128
  • ****
  • Posts: 289
  • Country: fr
  • My heart still runs on traditional CPC.
    • My code for the CPC.
  • Liked: 142
  • Likes Given: 351
Re: C programming using stdio on the CPC
« Reply #17 on: 00:19, 28 May 20 »
@cpcitor Not sure what happened, my guess is that my internet failed or I was not patient to wait for wget to finish (maybe it didn't show the progress).

Now the make finished and I check the helloworld, looks very nice!

Anyway, back to the topic :)

Happy to know it works.

I have added a long time ago some visual feedback in wget, at least in SDCC download, perhaps I forgot other places?

There is documentation since the beginning for first steps, on https://github.com/cpcitor/cpc-dev-tool-chain, including pages:
* "why this tool"
* "how to install"
* "use with custom project".

For the rest, IMHO currently it is best to apprach with an explorer's mind to really benefit: look at tests, look at https://github.com/cpcitor/color-flood-for-amstrad-cpc source code .

Everyone feel free to open issues on https://github.com/cpcitor/cpc-dev-tool-chain/issues . Any surprising behavior or lack of instructions about how to benefit from something *are* legitimate issues that I will solve.

Cheers!
Had a CPC since 1985, currently software dev professional, including embedded systems.

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