EmbDev.net

Forum: ARM programming with GCC/GNU tools Sharing Header files between C and Assembly (enum support)


von Tat W. (Company: Universiti Sains Malaysia) (tcwan)


Rate this post
useful
not useful
Hi,

I'm interested in (re)writing a bunch of drivers for a project in 
assembly, which will be called from C routines. The Compiler and 
Assembler are GNU gcc and as.

I plan to run the Assembly language files through the C pre-processor 
before actual assembly, so that a single header file can be shared 
between the assembly driver routines and the C interface to the routine.

However, I'm having a problem trying to find a good and clean approach 
for dealing with enums. Is there a way to define enums such that they 
can be referenced correctly in both Assembly and C, without duplicating 
it manually like in the following code fragment.
1
#ifndef __ASSEMBLY___ 
2
typedef enum { ENUM_A, ENUM_B, ... } enumvalues;
3
#else 
4
5
#define ENUM_A 0
6
#define ENUM_B 1
7
...
8
9
#endif

TIA

von Jörg W. (dl8dtl) (Moderator)


Attached files:

Rate this post
useful
not useful
No, there's no better way than that.  enums are a C language construct,
so they cannot be handled by an assembler.

I hacked a short Perl script that can automatically extract all enum
values from the stabs debugging information (-gstabs) of your object
file(s) (including the final ELF file), and return it as a sequence
of #define statements.  So you could use that to construct an
assembler-only include file supplying #define macros that match the
C file's enum values.

von Bartli (Guest)


Rate this post
useful
not useful
Perhaps something like this?
1
#ifdef __ASSEMBLY__
2
#define BEGIN_ENUM
3
#define ENUM_VAL(name, value) .equ name, value
4
#define END_ENUM(enum_name)
5
#else
6
#define BEGIN_ENUM typedef enum {
7
#define ENUM_VAL(name, value) name = value,
8
#define END_ENUM(enum_name) } enum_name;
9
#endif
10
11
12
BEGIN_ENUM
13
ENUM_VAL(A, 0)
14
ENUM_VAL(B, 1)
15
END_ENUM(foo)

von Bartli (Guest)


Rate this post
useful
not useful
Depending on your target CPU .equ's syntax may differ, refer to the gas 
manual. Also you might want to use .equiv instead of .equ, again, refer 
to the gas manual.

von Tat W. (Company: Universiti Sains Malaysia) (tcwan)


Rate this post
useful
not useful
Thanks for all the suggestions. I prefer Bartli's approach since it's 
done in a single pass. I suppose having to manually specify the enum 
value each time is a minor tradeoff.

von Jörg W. (dl8dtl) (Moderator)


Rate this post
useful
not useful
You have to be careful though to not specify two names for the
same value.   This is perfectly legal for a C enum, but probably
rather not what you intend.

von Bartli (Guest)


Rate this post
useful
not useful
Yes...on second thought I'd think twice before using my approach because 
it leaves you no choice but to assign every value manually.

Personally before I go and parse debug information to generate code I'd 
probably rather invent an own small DSL (using an existing scripting 
language, of course) and use that to define my data and have the C 
headers and the assembly source generated from it.

von Bartli (Guest)


Rate this post
useful
not useful
This is fun. Here's a version that doesn't require you to assign the 
values manually. You could easily extend this to provide full C enum 
support where the values optionally may be assigned.

1
#ifdef __ASSEMBLY__
2
#define BEGIN_ENUM .set last_enum_value, 0
3
.macro enum_val name
4
.equiv \name, last_enum_value
5
.set last_enum_value, last_enum_value + 1
6
.endm
7
#define END_ENUM(name)
8
#define ENUM_VAL(name) enum_val name
9
#else
10
#define BEGIN_ENUM typedef enum {
11
#define END_ENUM(name) } name;
12
#define ENUM_VAL(name) name,
13
#endif
14
15
BEGIN_ENUM
16
ENUM_VAL(A)
17
ENUM_VAL(B)
18
ENUM_VAL(C)
19
END_ENUM(foo)

von Tat W. (Company: Universiti Sains Malaysia) (tcwan)


Rate this post
useful
not useful
I've combined the two versions (to include assignment of values to 
enums), and renamed BEGIN_ENUM/END_ENUM to ENUM_BEGIN/ENUM_END for 
aesthetic reasons. In addition, I've put the macro definition outside of 
the #define ENUM_BEGIN since I'm not sure what happens if multiple 
ENUM_BEGINs are used (where the macro definition would be repeated).

Posted here for easy 'cut-n-paste'
1
#ifdef __ASSEMBLY__
2
3
4
  .set last_enum_value, 0
5
  .macro enum_val name
6
  .equiv \name, last_enum_value
7
  .set last_enum_value, last_enum_value + 1
8
  .endm
9
10
#define ENUM_BEGIN  .set last_enum_value, 0
11
12
#define ENUM_VAL(name) enum_val name
13
#define ENUM_VALASSIGN(name, value)            \
14
  .set last_enum_value, value                 ;\
15
  enum_val name
16
#define ENUM_END(enum_name)
17
18
#else
19
20
#define ENUM_BEGIN typedef enum {
21
#define ENUM_VAL(name) name,
22
#define ENUM_VALASSIGN(name, value) name = value,
23
#define ENUM_END(enum_name) } enum_name;
24
25
#endif

von Bartli (Guest)


Rate this post
useful
not useful
Cool. I actually didn't know you could use semicolons as line breaking 
character in gas, that's why I introduced the gas macro enum_value in 
the first place.

von Jörg W. (dl8dtl) (Moderator)


Rate this post
useful
not useful
Bartli wrote:
> Cool. I actually didn't know you could use semicolons as line breaking
> character in gas, ...

It's processor-dependent.  IIRC, the AVR uses semicolons as a comment
delimiter, and then offers dollar signs as alternate line breaking
characters instead.

Please log in before posting. Registration is free and takes only a minute.
Existing account
Do you have a Google/GoogleMail account? No registration required!
Log in with Google account
No account? Register here.