News:

Printed Amstrad Addict magazine announced, check it out here!

Main Menu
avatar_cpcitor

cpc-dev-tool-chain: a portable toolchain for C/ASM development targetting CPC.

Started by cpcitor, 20:01, 08 October 13

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

cpcitor

Quote from: arnoldemu on 09:35, 22 October 13
This may help you?

On linux I often use makefiles or shell scripts.

It already helped a little. I'm considering naming something.bin a naked binary, and something.amsdos.bin a binary with amsdos header. Not only to have make automatically figure out what to do, but also to help know at all times what a file contains.

In french we say "appeler un chat un chat", which would translate to "naming/calling 'cat' a cat".
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.

Munchausen

Quote from: andycadley on 20:04, 09 October 13
There's a mono port of msbuild (xbuild IIRC) so it's not platform specific, but the point wasn't "You should use X not Y" but rather that trying to start something like this by dictating "You must use X because that happens to be super convenient for me" is a complete non-starter.


I didn't see anyone saying you must use anything.


It's great to have something that doesn't need msbuild/windows/mono/wine or anything like that. I use haiku, which does not have mono or wine, and most UNIX/BSD users also prefer to have something native. For these users its great, for windows there are other things available.


Or did I miss something?

cpcitor

Hello all,

I have pushed another update of the cpc-dev-tool-chain kit on cpcitor/cpc-dev-tool-chain · GitHub .

Summary of changes:


  • For all sdcc projects, AMSDOS load and run address automatically computed from available compilation data.
  • All cpcrslib samples compile and run.
  • Fixed hello_world_using_sdcc (build was broken).

RAM programs built using a script don't need crt0

During that work I tried to simplify crt0, to the point I suppressed it completely and things became actually easier.

This confirmed that a plain "RAM Program"[nb]"RAM/ROM program" explained in Amstrad CPC Firmware guide chapter 10 at Soft968: CPC 464/664/6128 Firmware - CPCWiki .[/nb] does not actually need a crt0.

The crt0 seen in CPC context.

The crt0 everyone (H. Hansen, Stephbb75, cpcmania, norecess, cpcrslib[nb]Octoate's crt0.s for "ROM program" is a little cleaner.[/nb]) uses in the CPC world is a simplified ("broken") version of a generic one intended for a Z80-based device that starts without OS and needs a specific memory layout at address 0 (often a ROM).

For a RAM program on the CPC, the firmware takes care already. The effect of the crt0 everyone uses is to add constraints and force the programmer to manually adjust it to be consistent with sdcc's --code-loc argument. Removing the crt0 removes the constraint.

crt0 is useful in special cases, where the programmer needs particular memory layout.

Do I need a crt0 ?

I know three cases where a crt0 is useful :


  • As one of several ways to force the run address to a particular address. It can be done with a much shorter crt0 than the one used anyway. Without crt0 the build environment can extract address of main from symbol map (cpc-dev-tool-chain supports both).
  • You code a ROM program. crt0 is a good place to put the ROM header, as Octoate did.
  • You have lot of code, target CPC6128 and want put different part of C or ASM code in different RAM banks. You define the memory layout in crt0, assign different C/ASM source files to different memory areas.

Do I need to care ?

The aim of cpc-dev-tool-chain is to take care so that you don't have to and forget all this.

Next step

I should soon reach a point when I can document how to use the toolchain for a personal project.

Expect something like :

  • once, create a directory,
  • once, run a command to tell cpc-dev-tool-chain it's a project,
  • at will, type/adjust your C source and get a runnable file whenever you type make.

I see some interesting future extensions like :


  • Change from a RAM program to a ROM program just by chagning a line like PROGRAM_KIND in a conf file. But don't use self-modifying code.
  • Create a "RAM program" project and use a generic "ROM program that uncompresses a RAM program" project to make it a ROM. (Clean and compatible with self-modifying code.)

All feedback welcome.

Your feedback may drive the directions of development, as the suggestion to support cpcrslib did already.
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.

cpcitor

Hello,

Another update pushed.

Change summary

New features:


  • Integrated 2cdt and playtzx. Generating a VOC file is as easy as doing 'make voc'.
  • Convenience targets to build only what you want: bin dsk cdt ihx voc. For example, go to a project directory, type 'make dsk' and you've got an up-to-date DSK file ready for emulator. 'make all' does what you expect.
  • More information printed during build, may be useful if anyone has problems.
  • Global 'make sdcc-all' that allows to quickly check everything works before posting a release.
  • In cpcrslib you can 'make examples' to build all examples into DSK's at once.

Bugs fixed:


  • Running make on a project would recompile even if nothing changed. Fixed.
  • Fixed some missing dependency declarations. Their absence had no visible impact on basic build, but if you do a "make -j6 sdcc-all" on a fresh copy, it broke sometimes.

Cheers, :)
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.

cpcitor

Hello again,

I've updated the documentation. It now explains how to create a project on your own from scratch, then give hints at how easy it is to use cpcrslib instead of bloated stdio.

Homepage cpcitor/cpc-dev-tool-chain · GitHub
Custom project tutorial https://github.com/cpcitor/cpc-dev-tool-chain/blob/master/CDTC_with_custom_project.md



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

jrodriguezv

Wow! Wonderful!

I really love this project. Thanks for your job.

What do you think about adding an emulator as another tool? A great candidate could be cap32 (http://sourceforge.net/projects/caprice32/)

ralferoo

#31
Quote from: cpcitor on 11:28, 25 October 13
Do I need a crt0 ?

I know three cases where a crt0 is useful :


       
  • As one of several ways to force the run address to a particular address. It can be done with a much shorter crt0 than the one used anyway. Without crt0 the build environment can extract address of main from symbol map (cpc-dev-tool-chain supports both).
  • You code a ROM program. crt0 is a good place to put the ROM header, as Octoate did.
  • You have lot of code, target CPC6128 and want put different part of C or ASM code in different RAM banks. You define the memory layout in crt0, assign different C/ASM source files to different memory areas.[/l][/l][/l][/l][/l][/l]
Actually, the clue to what crt0 is for is in the name - it's there to initialise the C runtime. This means to pass the correct parameters to main (probably 0,NULL for CPC), initialise the memory allocator and standard IO streams. It's often used to also clear all BSS memory to 0 and to run any _atexit hooks after main finishes.

Given that the CPC doesn't have a memory allocator in the firmware, you'd almost certainly want to provide an implementation of malloc and free and crt0 is the only safe place to do such initialisation.

Also, in a traditional C system, crt0 generally wouldn't be used to force the run address - that's usually decided at link or load time. crt0 is unique only in that the linker knows to include it by default and so it specifies the entry point and is often also placed first.
[/list]

jrodriguezv

I've got an error compiling last version:

+ sdcc -mz80 --no-std-crt0 -Wl-u hello.rel fillscreen.rel km_wait_key.rel --code-loc 0x4000 --data-loc 0 ..//tool/cdtc_stdio/putchar_cpc.rel -o hellosdc.ihx
?ASlink-Error-<cannot open> : "..//tool/cdtc_stdio/putchar_cpc.rel"
make: *** [hellosdc.ihx] Error 1


cpcitor

Thanks for the report.

Can you manually go to the cdtc_studio directory, do "make" there and see if it then works ?
This will help me figure out what's wrong and how to fix.

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

jrodriguezv

Yes, it works!

[jose@carallan cdtc_stdio]$ make
BINS = cdtc_stdio.bin
CDTC_ENV_FOR_2CDT = ../..//tool/2cdt/build_config.inc
CDTC_ENV_FOR_CPC_PUTCHAR = ../..//tool/cdtc_stdio/cdtc_stdio.lib
CDTC_ENV_FOR_HEX2BIN = ../..//tool/hex2bin/build_config.inc
CDTC_ENV_FOR_IDSK = ../..//tool/idsk/build_config.inc
CDTC_ENV_FOR_PLAYTZX = ../..//tool/playtzx/build_config.inc
CDTC_ENV_FOR_SDCC = ../..//tool/sdcc/build_config.inc
CDTNAME = cdtc_stdio.cdt
CODELOC = 0x4000
DSKNAME = cdtc_stdio.dsk
GENHRDS =
HDRS =
IHXS = cdtc_stdio.ihx
LDFLAGS =
RELS =  putchar_cpc.rel
SRCS =
SRSS = putchar_cpc.s
TARGETS = cdtc_stdio.dsk cdtc_stdio.bin
VOCNAME = cdtc_stdio.voc
( . ../..//tool/sdcc/build_config.inc ; set -xv ; sdasz80 -l -o -s putchar_cpc.rel putchar_cpc.s ; )
+ sdasz80 -l -o -s putchar_cpc.rel putchar_cpc.s
( . ../..//tool/sdcc/build_config.inc ; set -euxv ; sdar rc "cdtc_stdio.lib" putchar_cpc.rel ; )
+ sdar rc cdtc_stdio.lib putchar_cpc.rel

After this I've been able to compile the example.  :)

cpcitor

Ok, thanks. It's a missing dependency. You'll need to do the same with cpcrslib directory. I'll fix that and the tests soon.
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.

jrodriguezv

OK, but I can't compile cpcrslib directory, I have errors on make all related to make examples:

../cpcrslib_SDCC/SDCC/TileMap.s:1248: Error: undefined symbol encountered during assembly
removing TileMap.rel
make[1]: *** [TileMap.rel] Error 2
make[1]: Leaving directory `/home/jose/code/cpc/cpc-dev-tool-chain-master/tool/cpcrslib/cpcrslib_SDCC.buildtree'
make: *** [cpcrslib_SDCC.buildtree/.built] Error 1

>

cpcitor

Thanks for the report.
I'll have to check what is at line 1248 of tilemap.s .
Can you post the complete make log of this part ?
E.g. in cpcrslib

make clean ; make 2>&1 | tee make.log

And post make.log content.

In future cases it can be useful to try make clean ; make then see if it fixes.
In any case it's interesting to know about it.

Regards.

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

jrodriguezv


cpcitor

Quote from: jrodriguezv on 16:05, 26 October 13
Here you have the log....

Thanks. I'm trying to reproduce the problem.

I just reproduced this:

Quote?ASlink-Error-<cannot open> : "..//tool/cdtc_stdio/putchar_cpc.rel"
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.

cpcitor

Quote from: jrodriguezv on 12:37, 26 October 13
?ASlink-Error-<cannot open> : "..//tool/cdtc_stdio/putchar_cpc.rel"

Just pushed an fix, this should not happen again.

Quote from: jrodriguezv on 14:31, 26 October 13
../cpcrslib_SDCC/SDCC/TileMap.s:1248: Error: u undefined symbol encountered during assembly

Trying to reproduce that one...
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.

cpcitor

Quote from: jrodriguezv on 16:05, 26 October 13
Here you have the log....

I have fixed the dependency on cpcrslib and did not encounter that problem.

Here's the part of my build log:

+ sdasz80 -o GphStrStd.rel ../cpcrslib_SDCC/SDCC/GphStrStd.s
( . "/mypath/cpc-dev-tool-chain"/tool/sdcc/build_config.inc ; set -xv ; sdasz80 -o TileMap.rel ../cpcrslib_SDCC/SDCC/TileMap.s ; )
+ sdasz80 -o TileMap.rel ../cpcrslib_SDCC/SDCC/TileMap.s
( . "/mypath/cpc-dev-tool-chain"/tool/sdcc/build_config.inc ; set -xv ; sdar rc cpcrslib.lib cpcrslib.rel GphStr.rel Sprites.rel Keyboard.rel UnExoOpt.rel Uncrunch.rel GphStrStd.rel TileMap.rel ; )
+ sdar rc cpcrslib.lib cpcrslib.rel GphStr.rel Sprites.rel Keyboard.rel UnExoOpt.rel Uncrunch.rel GphStrStd.rel TileMap.rel


Can you download a new fresh copy of the toolchain and build the hello project again ? To see if if happens again.

Please keep the old one in case, it may be interesting for a comparison.

I generated the hello.c with these commands :

{ echo "#include <stdio.h>" ; echo "int main() { printf(\"Hello World, hello $USER.\\n\"); while (1) {} ; return 0; }" ; } >hello.c
make

{ echo "#include <cpcrslib.h>" ; echo "int main() { cpc_PrintStr(\"Hello World, hello $USER.\\n\"); while (1) {} ; return 0; }" ; } >hello.c
make


Regards,

EDIT: adjusted capitalization. It's cpc_PrintStr not cpc_printStr.
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.

cpcitor

    Quote from: ralferoo on 09:45, 26 October 13
    Actually, the clue to what crt0 is for is in the name - it's there to initialise the C runtime. This means to pass the correct parameters to main (probably 0,NULL for CPC), initialise the memory allocator and standard IO streams. It's often used to also clear all BSS memory to 0 and to run any _atexit hooks after main finishes.

    Given that the CPC doesn't have a memory allocator in the firmware, you'd almost certainly want to provide an implementation of malloc and free and crt0 is the only safe place to do such initialisation.

    Also, in a traditional C system, crt0 generally wouldn't be used to force the run address - that's usually decided at link or load time. crt0 is unique only in that the linker knows to include it by default and so it specifies the entry point and is often also placed first.
    [/list]

    You're right in many aspects. I had to be short and specific to SDCC and the CPC.

    One important thing is also to ensure global variables are initialized.

    Looks like no one on the C CPC scene (even Octoate in his crt0 targeting ROM) has taken care of initialized non-const static/global variables.
    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.

    cpcitor

    New update

    A new test project showing:


    • how global, static and local variables, both const and non-const are handled (what area they land on, what symbols they cause)
    • that C-level preprocessor works fine, including stringification of macro arguments, which comes very handy for debug print
    • a crt0 that ensures initialized variables are indeed initialized.

    See result below. All initialized values should be 42 always. Without gsinit some are zero.

    Without GSINIT, line 3 and 4 have value 0.

    [attachimg=1]

    With GSINIT, line 3 and 4 have expected value 42.

    [attachimg=2]

    To have them initialized I have written a short but heavily commented crt0.s .
    I post a copy below, for didactic value, of c_features_test.c and crt0.s.

    I'll make it a default crt0 for RAM programs. In the meantine, if you need global initialized variables just copy it in your project.

    You can find the content on cpcitor/cpc-dev-tool-chain · GitHub as usual.

    #include <stdio.h>

    // This file is used to test and clarifies what variables become.

    // ## Symbols ?

    // ## Symbols and global/static variables
    // As expected, sdcc create a global symbol for global variables, not for static variable.
    // .globl _int_initialized
    // .globl _int_uninitialized
    // .globl _const_int_initialized

    // ## Symbols and local variables
    // Local variables are allocated on the stack.
    // No symbol (even a local one) is generated for a local variable.

    // sdcc puts a (global or static) int uninitialized to DATA area
    static int static_int_uninitialized;
    int int_uninitialized;
    // becomes :
    // ;--------------------------------------------------------
    // ; ram data
    // ;--------------------------------------------------------
    // .area _DATA
    // _static_int_uninitialized:
    // .ds 2
    // _int_uninitialized::
    // .ds 2

    // sdcc puts a (global or static) int initialized to INITIALIZED area
    static int static_int_initialized = 42;
    int int_initialized = 42;
    // becomes :
    // ;--------------------------------------------------------
    // ; ram data
    // ;--------------------------------------------------------
    // .area _INITIALIZED
    // _static_int_initialized:
    // .ds 2
    // _int_initialized::
    // .ds 2

    // .area _CODE
    // .area _INITIALIZER
    // __xinit__static_int_initialized:
    // .dw #0x002A
    // __xinit__int_initialized:
    // .dw #0x002A
    // .area _CABS (ABS)

    // So, for global variables to be correctly initialized, crt0 has to copy _INITIALIZER section to _INITIALIZED.



    // sdcc puts a (global or static) int initialized to INITIALIZED area
    static const int static_const_int_initialized = 42;
    const int const_int_initialized = 42;

    // _static_const_int_initialized:
    // .dw #0x002A
    // _const_int_initialized:
    // .dw #0x002A


    // SDCC refuses to compile uninitialized const int. It does not make great sense but gcc accepts.
    //static const int static_const_int_uninitialized;
    //const int const_int_uninitialized;





    int main() {

    int local_int_uninitialized;
    int local_int_initialized = 42;

    const int const_local_int_uninitialized;
    const int const_local_int_initialized = 42;

    #define PRINT_VAR(x) printf( "%d\t= " #x "\r\n", x);
    #define PRINT_PTR(x) printf( "%p\t<- " #x "\r\n", &x);

    #define PRINT_BOTH(x) PRINT_VAR(x); PRINT_PTR(x);

    PRINT_VAR(static_int_uninitialized);
    PRINT_VAR(int_uninitialized);

    PRINT_VAR(static_int_initialized);
    PRINT_VAR(int_initialized);

    PRINT_VAR(static_const_int_initialized);
    PRINT_VAR(const_int_initialized);

    //PRINT_VAR(static_const_int_uninitialized);
    //PRINT_VAR(const_int_uninitialized);

    PRINT_VAR(local_int_uninitialized);
    PRINT_VAR(local_int_initialized);
    PRINT_VAR(const_local_int_uninitialized);
    PRINT_VAR(const_local_int_initialized);



    PRINT_PTR(static_int_uninitialized);
    PRINT_PTR(int_uninitialized);

    PRINT_PTR(static_int_initialized);
    PRINT_PTR(int_initialized);

    PRINT_PTR(static_const_int_initialized);
    PRINT_PTR(const_int_initialized);

    //PRINT_PTR(static_const_int_uninitialized);
    //PRINT_PTR(const_int_uninitialized);

    PRINT_PTR(local_int_uninitialized);
    PRINT_PTR(local_int_initialized);
    PRINT_PTR(const_local_int_uninitialized);
    PRINT_PTR(const_local_int_initialized);

    return 0;
    }


    ;; crt0.s - A crt0 in Z80 assembler language targeting Amstrad CPC

    ;; Copyright (C) 2013 Stéphane Gourichon / cpcitor

    ;  This library is free software; you can redistribute it and/or modify it
    ;  under the terms of the GNU General Public License as published by the
    ;  Free Software Foundation; either version 2, or (at your option) any
    ;  later version.
    ;
    ;  This library is distributed in the hope that it will be useful,
    ;  but WITHOUT ANY WARRANTY; without even the implied warranty of
    ;  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    ;  GNU General Public License for more details.
    ;
    ;  You should have received a copy of the GNU General Public License
    ;  along with this library; see the file COPYING. If not, write to the
    ;  Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston,
    ;   MA 02110-1301, USA.
    ;
    ;  As a special exception, if you link this library with other files,
    ;  some of which are compiled with SDCC, to produce an executable,
    ;  this library does not by itself cause the resulting executable to
    ;  be covered by the GNU General Public License. This exception does
    ;  not however invalidate any other reasons why the executable file
    ;   might be covered by the GNU General Public License.
    ;--------------------------------------------------------------------------

    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;; Since this will be used by CPC coders, which are usually
    ;; not savvy of C compiling/linking/init internals, this file
    ;; is abundantly commented.
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

    ;; The module name appears in many compiler/linker log files,
    ;; so it's important to define it.

    .module crt0


    ;; We will reference the C-level symbol "main".
    ;; The line below is equivalent to C-level "extern void main();"

    .globl _main



    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;; Do we need an absolutely positioned HEADER area ?
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

    ;; Short answer: no for a RAM program.

    ;; Most z80 crt0 start with dedicating 0x38 bytes to interrupt
            ;; vectors. This makes sense only when making an image that
            ;; starts at address 0, which is not the case for a CPC RAM
            ;; program or upper ROM program.
    ;; This crt0 targets a RAM program. So nothing to do at this step.


    ;; Creating absolutely positioned linker areas would just put
    ;; constraints and yield more maintenance work.

    ;; We can avoid that and just let the linker pack areas one
    ;; after the other.  So, no ".org 0x" here. The simplest thing
    ;; to do with SDCC's Z80 target is to set location at only one
    ;; place : the --code-loc option of sdcc.

    ;; Hence, we start with the CODE area to ensure we start at
    ;; the requested location.

    .area _CODE

    cpc_run_address::
    init:
            ;; Initialise global variables, see below.
            call    gsinit
    jp _main

    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;; Do we need an _exit symbol ?
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

    ;; Short answer: not confirmed, so not done.

    ;; SDCC's default crt0 defines a _init symbol that does "ld a,#0 ; rst 0x08".
    ;; This is supposed to allow the C-level exit() function to do what people expect.
    ;; This won't work on the CPC.

    ;; We have three choices :

    ;; - just don't implement exit()

    ;; - implement it with a "ret". This would enable calling
    ;; "exit(value);" form main(). It has limited interest because
    ;; "return value;" already works (FIXME check which register
    ;; holds return value). Unfortunately, from another C function
    ;; it would just return from the current function, not exit
    ;; the program. So, not really useful.

    ;; - implement it with "rst 0x00". This should reset the CPC.

    ;; - record stack pointer before calling _main, put it back on
    ;; _exit, set the return value in register and "ret". That
    ;; would work if the C code has not killed the firmware RAM
    ;; area.

    ;; That last option would be useful especially when using
    ;; cpc-dev-tool-chain to implement some RSX extensions that
    ;; need to call exit().

    ;; Until the need is confirmed we do nothing.



    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;; Initialize global variables.
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

    ;; Why must we care ? Else global variables are not
            ;; initialized.

    ;; How this happens ?

    ;; Compiler does not assume that compiled output can be a RAM.

    ;; Indeed, if all compiled data lands in a ROM so is
    ;; initialization values. Then we must copy them to RAM.

    ;; What SDCC does: run-time access to initialized global
    ;; variables is through area named _INITIALIZED (should be
    ;; somewhere in RAM).

    ;; Initial values of initialized global variables are provided
    ;; in another region named _INITIALIZER.


    ;; * "ROM program" case

    ;; This makes total sense if linker output lands in a ROM. The
    ;; only option is to copy _INITIALIZER to _INITIALIZED (modulo
    ;; some possible compression tricks).


    ;; * "RAM program" case

    ;; If linker output already lands in RAM, as in a CPC "RAM
            ;; program", this works also but wastes an amount of RAM equal
            ;; to the amount of initialized data.

    ;; We may write an external script to trick the linker to
    ;; allocate both area in an absolute fashion to the same
    ;; address. No wasted bytes, no copy.

    ;; Note that using unitialized global variable and
            ;; initializing them in a function does not solve: the same
            ;; amount of memory is wasted anyway, it has to be maintained
            ;; manually.

    ;; Using function-local variables is worse: they are accessed
            ;; through the stack which is slower (FIXME unless some SDCC
            ;; compilation tricks are used ?).

    ;; Conclusion

    ;; So far we do simple and waste some bytes, that's ok.

    ;; If/when need is confirmed, the trick to absolutely position
    ;; both area at same position may be used.  Or tell the
    ;; compiled that code is in RAM ? FIXME Write that to
    ;; sdcc-devel mailing-list.

    .area   _GSINIT

    .globl l__INITIALIZER
    .globl s__INITIALIZED
    .globl s__INITIALIZER

    gsinit::
    ld bc, #l__INITIALIZER
    ld a, b
    or a, c
    jr Z, gsinit_next
    ld de, #s__INITIALIZED
    ld hl, #s__INITIALIZER
    ldir
    gsinit_next:

    .area   _GSFINAL
    ret

    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.

    cpcitor

    Hello,

    Some news. Another update to github cpcitor/cpc-dev-tool-chain · GitHub .

    Can we write C without fear of bloat ?

    Isn't it a bit frightening to have a 3kb executable for a hello world ? Well, that's because we used the default SDCC printf_large. Lean is good. Can we stay lean ?

    Can we get this from a 203-byte binary from a simple C source ?
    [attachimg=1]

    Answer: yes !

    Auto cherry-pick firmware routines.

    It's handy to call the firmware for some simple services, at the beginning/end of a program, of when performance does not matter that much.

    You wouldn't like if using a single function from C would suddenly bloat your executable, right ?

    I was looking for a C to firmware interface and found Kevin's adaptation for contiki. It covers mostly text functions, and 5 graphic functions. It worked but using one function pulled the whole amsgraph or conio to the final program. I want the dev toolchain to be lean.

    The trick is simple : define symbols in independent source file (unless they are obviously needed together as a chole). Define a different .module for each sourced file.

    The linker will automagically pick the necessary modules but not the unneeded ones. Nice.  8)

    So I packaged a few common firmware routines and adjusted the makefiles.

    find .
    ./Makefile
    ./src
    ./src/fw_gra_line_absolute.s
    ./src/fw_gra_set_pen.s
    ./src/fw_km_wait_key.s
    ./src/cfwi_txt_str0_output.s
    ./src/fw_gra_move_absolute.s
    ./src/fw_scr_set_mode.s
    ./src/fw_km_read_char.s
    ./src/fw_gra_get_pen.s
    ./src/fw_gra_plot_absolute.s
    ./cdtc_project.conf
    ./include
    ./include/cfwi
    ./include/cfwi/cfwi.h
    ./include/cfwi/fw_km.h
    ./include/cfwi/fw_scr.h
    ./include/cfwi/fw_gra.h
    ./include/cfwi/cfwi_txt.h


    It's easy to use : just #include <cfwi/cfwi.h> and you can use the functions appearing in the .h files.

    For an example, the following screen

    [attachimg=1]

    is produced by a 203-byte executable  :) from this simple source :


    #include "cfwi/cfwi.h"

    void
    main ()
    {
    {
    static unsigned int x, y;

    for (x = 0; x < 255; x++)
    {

    for (y = 0; y < 255; y++)
    {
    if ((x ^ y) > x)
    {
    fw_gra_plot_absolute(2 * x + 1, 2 * y + 1);
    }
    }

    }
    }

    cfwi_txt_str0_output("Press any key to exit.");
    fw_km_wait_key();
    }


    It could be instantaneous with optimized routines, but that's not the point, which is ease of use without bloat.
    Instead it runs in seconds, because it calls GRA_PLOT_ABSOLUTE thousands of times.
    It's definitely much faster than BASIC and probably leaner anyway.
    QED.
    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.

    cpcitor

    Quote from: cpcitor on 22:41, 28 October 13
    The linker will automagically pick the necessary modules but not the unneeded ones. Nice.  8)

    This can be checked by looking at the .map file of the executable after compilation.
    It lists all the symbols that are actually present in the final binary.

    By the way, CFWI means C-firmware-interface.

    There are other news with this update:

    Compiler flags

    You can set project-wide compiler flags like this in cdtc-project.conf:

    CDTC_ROOT=(automatically set by new-sdcc-project.sh)
    CFLAGS=--fomit-frame-pointer
    PROJNAME=004Music
    CODELOC=0x4000


    I could probably support per-file compiler flags if requested.

    Omit frame pointer ?

    By default SDCC maintains IX synchronized with SP. It's a little more work but allows to fetch and store local variables more easily.

    With --fomit-frame-pointer, SDCC uses directly SP when appropriate, or sets up IY temporarily.

    Code can be shorter or longer depending on its complexity (local compitation or passing many args to functions).

    I don't use it so far.

    Malloc works

    I have seed that by default, sdcc provides a working malloc implementation with a 1k heap. So, malloc is already working... for small needs. For bigger needs, one should copy sdcc's _heap.c and for example add to CFLAGS:  "-D HEAP_SIZE=2048"

    Local dependencies not fully done

    Inter-file dependencies are so far not computed.
    In practice it means that changing cfwi does not trigger a recompute of a project that depends on it.
    In practice you can force a recompile by touching or doing a trivial change to any .c or .s file in the project directory.

    I'll probably follow Advanced Auto-Dependency Generation so that you don't have to.

    As always, feedback welcome.

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

    Bruce Abbott

    QuoteWith --fomit-frame-pointer, SDCC uses directly SP when appropriate, or sets up IY temporarily.
    You can also use --fno-omit-frame-pointer to force using the IX register exclusively.

    I examined the results of both options with a small test function:-
    unsigned char test(unsigned char x){
        unsigned char y;
        y = x + 1;
        x = y - 1;
        return x;
    }


    With --fomit-frame-pointer it used both IY and HL to access variables on the stack...
    _test: ; --fomit-frame-pointer
    ;code.c:99: y = x + 1;
        ld    iy,#2
        add    iy,sp
        ld    a,0 (iy)
        inc    a
    ;code.c:100: x = y - 1;
        ld    hl,#2
        add    hl,sp
        add    a,#0xFF
        ld    (hl),a
    ;code.c:101: return x;
        ld    l,0 (iy)
        ret


    With --fno-omit-frame-pointer it used IX...
    _test: ; --fno-omit-frame-pointer
        push    ix
        ld    ix,#0
        add    ix,sp
    ;code.c:99: y = x + 1;
        ld    a,4 (ix)
        inc    a
    ;code.c:100: x = y - 1;
        add    a,#0xFF
    ;code.c:101: return x;
        ld    4 (ix), a
        ld    l, a
        pop    ix
        ret


    I have been trying to get demo 004 (004 - Music, SFX & Keyboard) to work properly. It would play the music and sound effects OK, but the ball sprite just flickered. Pressing an arrow key to change the music either didn't work or crashed.

    Compilation was very slow (up to 5 minutes) making debugging a pain, apparently related to the external references in calls to cpc_WyzInitPlayer(). I fixed that by reducing --max-allocs-per-node to 100.

    I found out that SDCC normally uses the IX register to access variables on the stack, so it must be preserved across function calls. However, several functions in cpcrslib.lib and cpcwyzlib.lib (eg. _cpc_PrintStr, _cpc_WyzInitPlayer) modify the IX register without saving and restoring it, resulting in the main program losing track of its variables!

    ETA:  --fomit-frame-pointer is the answer, as it forces the compiler to avoid the IX register. Music demo is now working 100%!

    cpcitor

    Quote from: Bruce Abbott on 03:03, 29 October 13
    You can also use --fno-omit-frame-pointer to force using the IX register exclusively.

    I have been trying to get demo 004 (004 - Music, SFX & Keyboard) to work properly. It would play the music and sound effects OK, but the ball sprite just flickered. Pressing an arrow key to change the music either didn't work or crashed.

    I saw this, too. Also sample 3 has last line of bottom sprite shifted to the right when compiled without omit-frame-pointer.

    Quote from: Bruce Abbott on 03:03, 29 October 13
    Compilation was very slow (up to 5 minutes) making debugging a pain, apparently related to the external references in calls to cpc_WyzInitPlayer(). I fixed that by reducing --max-allocs-per-node to 100.

    I noticed that this sample only takes eons to compile and suspected a bug in SDCC.
    How did you figure out that it's related to external references ?

    It's that part that takes a long time to compile:

    sdcc -mz80 -I/mypath/cpc-dev-tool-chain/cpclib/cpcrslib/cpcrslib_SDCC.installtree/include --fomit-frame-pointer -c code.c


    Quote from: Bruce Abbott on 03:03, 29 October 13
    I found out that SDCC normally uses the IX register to access variables on the stack, so it must be preserved across function calls. However, several functions in cpcrslib.lib and cpcwyzlib.lib (eg. _cpc_PrintStr, _cpc_WyzInitPlayer) modify the IX register without saving and restoring it, resulting in the main program losing track of its variables!

    ETA:  --fomit-frame-pointer is the answer, as it forces the compiler to avoid the IX register. Music demo is now working 100%!

    Congratulations for finding this.

    Actually, --fomit-frame-pointer is now default (since yesterday 14:27 UTC) for samples 3 and 4. It did fix sample 3 but not 4 for me.  :-[

    This shows that several contracts are possible between C and ASM code and they should not be mixed without care.


    How did you figure out that slow compilation is related to external references ?

    Cheers,  :)
    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.

    Bruce Abbott

    Quote from: cpcitor on 16:19, 29 October 13
    How did you figure out that slow compilation is related to external references ?
    Actually it's a red herring. The 'slow' lines all included functions taking externally defined variables which just 'happened' to be arrays. I did some more investigation  and found out this was not a coincidence. Bringing the array definitions into the main code made no difference, but changing them from arrays to plain ints/chars cut compilation time from 167 seconds to 45 seconds. 

    In this case 'optimization' of arrays seems to be a complete waste of time, because the code generated was identical. However I don't think kludging the source code is the answer. IMO the best solution is to simply apply --max-allocs-per-node with a small value.     

    cpcitor

    Hello,

    Summary: cpc-dev-tool-chain updated

    I'm working again on a CPC project.
    During that project I've hit bugs in iDSK and changed the workflow accordingly.
    Since everything is scripted in cpc-dev-tool-chain, I updated it so that all users can benefit.

    Here's a summary of recent changes:

    * now using SDCC 3.4.0 instead of 3.3.0
    * don't use iDSK by default but use Arnoldemu's addhead (to add a AMSDOS header to plain files) and cpcxfs (to put files into a DSK image). iDSK still available with make PREFER_IDSK_OVER_CPCXFS=1
    * various fixes (make clean infinite loop, missing dependencies causing some files to not be rebuilt)
    * works on Ubuntu 14.04

    Work in progress (not really visible yet):
    * work in progress on some more flexibility, allowing projects to refer to source tree in other locations. The benefit will be to compile and run from the same C source trees some cpc project and some test/debug code that calls the same routines. Allows to have full benefit of modern development environment, run test suite, etc.
    * work in progress on some more flexibility, allowing to generate one DSK containing files from several targets

    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.

    Powered by SMFPacks Menu Editor Mod