Author Topic: Sharing constants between C and ASM with SDCC  (Read 1763 times)

0 Members and 1 Guest are viewing this topic.

Offline tulinmola

  • CPC464
  • **
  • Posts: 7
  • Country: es
  • Liked: 27
  • Likes Given: 10
Sharing constants between C and ASM with SDCC
« on: 13:08, 08 August 18 »
Hi there! I have a simple question – I guess.


Let me illustrate this with an example. Suppose we have these files:


Code: [Select]
// renderer.h
...
#define RENDERER_WIDTH nn
...
extern void renderer_drawSprite(...);
...


Code: [Select]
// renderer_drawSprite.h
.equ RENDERER_WIDTH, nn
...


As you can see the RENDERER_WIDTH constant appears twice. And that's not good... At least I don't like it very much. So, my question is, how do you share constants between C and ASM code? Is there any "best practice" here?


Thanks in advance!
Tulo

Offline freemac

  • 464 Plus
  • *****
  • Posts: 374
  • Country: 00
  • Liked: 268
  • Likes Given: 272
Re: Sharing constants between C and ASM with SDCC
« Reply #1 on: 13:35, 08 August 18 »
#define does not create variable (/constant), it's just a "rename this into that" done even before compiling C (#, it's preprocessing instructions)

When you compile the .c, you can see the .asm build after.

Using global variable (outside of function) is good practice in SDCC Z80, you can also use struct.

.equ does not create variable (/constant), also, you can find them in some Targhan .asm for compiling music routine with or else without sound effect routine, building then a smaller or larger .bin file at result. So once again, here it's just a "rename this into that" done even before assembling ASM.

You can more dynamically write ASM from C using __asm__() function :
__asm__("NOP\nNOP\nNOP\nNOP\nNOP\n NOP\nNOP\nNOP\nNOP\nNOP\n NOP\nNOP\nNOP\n");
« Last Edit: 13:49, 08 August 18 by freemac »

Offline ronaldo

  • Dev
  • 6128 Plus
  • *****
  • Posts: 648
  • Country: es
    • Profesor Retroman
  • Liked: 941
  • Likes Given: 827
Re: Sharing constants between C and ASM with SDCC
« Reply #2 on: 14:56, 08 August 18 »
As you can see the RENDERER_WIDTH constant appears twice. And that's not good... At least I don't like it very much. So, my question is, how do you share constants between C and ASM code? Is there any "best practice" here?
To make you feel better (or not), I stumbled upon this problem two years ago and have not found a proper solution yet. Depending on the context, you may design your own workarounds. For instance, you may create a macro that generates both a #define and a piece of assembly. Something like this (warning: untested)
Code: [Select]
#define DEFINECONSTANT(C,VAL) \
  #define C VAL \
  void dummyfunction_##C##_VAL() __naked { \
   __asm { \
      .equ C, VAL \
   }__endasm; \
  }
It is the best I can think of by now.
« Last Edit: 20:05, 08 August 18 by ronaldo »

Online cpcitor

  • The user previously known as FindYWay
  • 464 Plus
  • *****
  • Posts: 322
  • Country: fr
  • My heart still runs on traditional CPC.
    • My code for the CPC.
  • Liked: 163
  • Likes Given: 419
Re: Sharing constants between C and ASM with SDCC
« Reply #3 on: 06:18, 06 December 19 »
Hi everyone. Old post, but interesting, and new information!

As you can see the RENDERER_WIDTH constant appears twice. And that's not good... At least I don't like it very much. So, my question is, how do you share constants between C and ASM code? Is there any "best practice" here?

Good question tulinmola, thanks for asking!
I am also very sensitive to the DRY principle (don't repeat yourself), SSOF (sigle source of truth) etc.

I see two uses for this:

* sharing memory addresses between asm and C, used to pass data. This is already well taken care of by Assembler and linker (declare extern on one side, declare global with value on the other). But it's not memory addresses that we want: we want constants.
* constants that can be used both sides to generate compact fast code, no indirections.

There are extra questions, like: ASM only knows integer values, but C can use #define (basically text) but also typed values (signed/unsigned integers, pointer), etc. Could we share values and get them typed on the C side?

To make you feel better (or not), I stumbled upon this problem two years ago and have not found a proper solution yet. Depending on the context, you may design your own workarounds. For instance, you may create a macro that generates both a #define and a piece of assembly. Something like this (warning: untested)
Code: [Select]
#define DEFINECONSTANT(C,VAL) \
  #define C VAL \
  void dummyfunction_##C##_VAL() __naked { \
   __asm { \
      .equ C, VAL \
   }__endasm; \
  }
It is the best I can think of by now.

Unfortunatey, by design preprocessor macros cannot generate #define directives. This is similar to question https://stackoverflow.com/questions/860273/macro-producing-macros-in-c , answer there is negative.

Yet there is hope!

I've been facing the problem in a small project I'm doing (small path towards a greater goal) and think I may have found a solution that looks neat. It should allow to:
(1) define named constants,
(2) use them as compile-time constants in ASM as well as in C,
(3) allow to define some in reference to others if needed,
(4) allow to use them as parameters to macro calls (both assembly macros and C macros)
(5) if at all useful, also be available as typed (well, only one type, unsigned 16bit, but thats what they are anyway). -- I suspect the compiler will always to a better optimization with good old #defined values than with typed values anyway.

I'll tell how it goes.
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.

Online cpcitor

  • The user previously known as FindYWay
  • 464 Plus
  • *****
  • Posts: 322
  • Country: fr
  • My heart still runs on traditional CPC.
    • My code for the CPC.
  • Liked: 163
  • Likes Given: 419
Re: Sharing constants between C and ASM with SDCC
« Reply #4 on: 03:33, 07 December 19 »
I've been facing the problem in a small project I'm doing (small path towards a greater goal) and think I may have found a solution that looks neat. It should allow to:
(1) define named constants,
(2) use them as compile-time constants in ASM as well as in C,
(3) allow to define some in reference to others if needed,
(4) allow to use them as parameters to macro calls (both assembly macros and C macros)
(5) if at all useful, also be available as typed (well, only one type, unsigned 16bit, but thats what they are anyway). -- I suspect the compiler will always to a better optimization with good old #defined values than with typed values anyway.

I'll tell how it goes.

Okay, it's mostly working but not really ready.
It works very well to propagate "simple" integer constants globally, à la #define.

Propagating typed values naively is not good because SDCC generates code and allocates memory to store the value. Perhaps this is so that the user can always take a pointer to that value.
Anyway this leads to inefficient Z80 code for typed values.
Code is very good for values propagates ad #define.

I'm continuing to think through this. I believe there can be a simple and natural way to get typed values. Something that allows to never repeat oneself yet share variables easily, with types if needed. Will find it.
« Last Edit: 03:59, 07 December 19 by cpcitor »
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.

Offline reidrac

  • Supporter
  • 6128 Plus
  • *
  • Posts: 960
  • Country: gb
  • Trying to gamedev!
    • index.php?action=treasury
    • usebox.net
  • Liked: 1749
  • Likes Given: 965
Re: Sharing constants between C and ASM with SDCC
« Reply #5 on: 10:10, 07 December 19 »
The #defines are processed by the cpp (C pre processor), so they only exist in C land. You can use them in ASM if you embed the ASM code.

For example:
Code: [Select]
#define CONSTANT 5

void fn()
{
  __asm;
    ld bc, #CONSTANT
  __endasm;

But I don't think you can have an include file that can be used in both C and ASM files, basically because the cpp won't process those ASM files.

I use __asm and __endasm sometimes when I'm converting from C to ASM, or because they are small pieces of ASM in an otherwise C code module. If I'm writing an ASM file, those tend to be independent and I try to avoid sharing constants (although sharing variables is OK, I tend to avoid that because it usually means a worse design).

My two cents.

EDIT: I'm not sure if I understand completely your problem. Just in case, you can do this...

Code: [Select]
// main.c
extern unsigned char shared;

unsigned char is_shared_zero();

void main()
{
   shared = 1;
   is_shared_zero(); // this is false
}

Code: [Select]
// module.z80
.globl _shared ; this will export _shared in asm and shared (no leading underscore) in C
.globl _is_shared_zero

_is_shared_zero::
  ld l, #0
  ld a, (_shared)
  or a
  ret nz
  ld l, #1
  ret
« Last Edit: 10:19, 07 December 19 by reidrac »
Released The Return of Traxtor, Golden Tail, Magica, The Dawn of Kernel, Kitsune`s Curse and Brick Rick for the CPC.

If you like my games and want to show some appreciation, you can always buy me a coffee.