Alex
Alex

Reputation: 896

C/C++ macro to repeat code

is there any way to repeat a C code N times with a macro? Also N is a macro.
For example if I have this macros:

#define N 5  
#define COODE "nop\n\t"
#define REPEAT [...]

When I call repeat the preprocessor writes CODE N times, so

 __asm__(REPEAT);

would became

__asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t");

I have an Arduino that have to wait for an exact (and small, about 10-15) number of clock. Each "nop" (no operation) takes exactly 1 clock cycle to be executed, and it does nothing. I can't just do a cycle, because each cycle is executed in more than one operation (initialize the counter, increment the counter, check if reached end), so instead of writing manually "nop\n\t" I'd like to have a macro. This way I can also simply change N to modify the program without rewriting it.

Thank you in advance

Upvotes: 12

Views: 13662

Answers (5)

Jay Miller
Jay Miller

Reputation: 2234

Boost has Boost.Preprocessor which does this among other things. Try BOOST_PP_REPEAT.

For your code:

#include <boost/preprocessor/repetition/repeat.hpp>

#define OP(z, n, text) text
...
__asm__( BOOST_PP_REPEAT(5, OP, "noop\n"\t);

Upvotes: 2

debug
debug

Reputation: 1079

The following code works with GNU C,

#define NOP __asm__("nop")

#define ten(a)     a;a;a;a;a;a;a;a;a;a
#define hundred(a) ten(ten(a))


int
main()
{
    hundred(NOP);
    return 0;
}

Compile and debug:

code@lab:~/debug$ gcc -g -o debug_NOP debug_NOP.c
code@lab:~/debug$ gdb -q --nh debug_NOP
Reading symbols from debug_NOP...done.
(gdb) set disassembly-flavor intel
(gdb) start
Temporary breakpoint 1 at 0x664: file debug_NOP.c, line 10.
Starting program: /home/code/debug/debug_NOP 

Temporary breakpoint 1, main () at debug_NOP.c:10
10      hundred(NOP);
(gdb) disassemble 
Dump of assembler code for function main:
   0x0000555555554660 <+0>: push   rbp
   0x0000555555554661 <+1>: mov    rbp,rsp
=> 0x0000555555554664 <+4>: nop
   0x0000555555554665 <+5>: nop
   0x0000555555554666 <+6>: nop
   0x0000555555554667 <+7>: nop
   0x0000555555554668 <+8>: nop
   0x0000555555554669 <+9>: nop
   ....
   0x00005555555546c6 <+102>:   nop
   0x00005555555546c7 <+103>:   nop
   0x00005555555546c8 <+104>:   mov    eax,0x0
   0x00005555555546cd <+109>:   pop    rbp
   0x00005555555546ce <+110>:   ret    
End of assembler dump.

Upvotes: 5

Peter Frost
Peter Frost

Reputation: 561

Assuming you're using the GNU toolchain, you can use the repeat directive of the gnu assembler as https://sourceware.org/binutils/docs/as/Rept.html

#define repeat(instruction, num) \
    asm volatile(                \
        ".rept " #num "\n\t"     \
        instruction "\n\t"       \
        ".endr\n\t");

int main() {
    repeat("nop", 5);
}

Produces the assembly:

 push   rbp
 mov    rbp,rsp
 nop
 nop
 nop
 nop
 nop
 mov    eax,0x0
 pop    rbp
 ret

https://godbolt.org/z/j8dzYvM7c

Upvotes: 1

qbolec
qbolec

Reputation: 5134

Based on Chris A's answer, here's a bit simpler approach which also seems to work for me:

template< unsigned N > 
inline static void nops(){
    asm ("nop");
    nops< N - 1 >();
}

template<> inline void nops<0>(){};

void setup() {
  nops<10>();
}

https://godbolt.org/z/a7MMea

Upvotes: 1

Chris A
Chris A

Reputation: 1485

If you want to do it without including a whole library or using define, you can use a simple recursive template:

//By Christopher Andrews, released under MIT licence.

template< unsigned N > struct Nops{
  static void generate() __attribute__((always_inline)){
    __asm__ volatile ("nop");
    Nops< N - 1 >::generate();
  }
};
template<> struct Nops<0>{ static inline void generate(){} };

void setup() {
  Nops<10>::generate();
}

void loop(){}

This will generate the exact number of nop's required.

0000010a setup:
10a: 00 00 nop
10c: 00 00 nop
10e: 00 00 nop
110: 00 00 nop
112: 00 00 nop
114: 00 00 nop
116: 00 00 nop
118: 00 00 nop
11a: 00 00 nop
11c: 00 00 nop
11e: 08 95 ret

I have used this method in a TFT driver for Arduino.

EDIT:

There is another way to easily do this on an AVR compiled with avr-gcc. I'm assuming this may not be available on older toolchains.

In order to delay execution for a specific number of cycles, GCC implements

void __builtin_avr_delay_cycles (unsigned long ticks) ticks is the number of ticks to delay execution. Note that this built-in does not take into account the effect of interrupts which might increase delay time. ticks must be a compile time integer constant; delays with a variable number of cycles are not supported

From here: https://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/AVR-Built_002din-Functions.html

Upvotes: 13

Related Questions