C/C++ macro to repeat code
Asked Answered
K

5

12

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

Kirtley answered 17/11, 2014 at 22:57 Comment(8)
Figure out the time it takes to set up a loop, and the time to do one iteration... if the time needed is larger that the sum, do the setup plus as many iterations as needed to almost reach your total, then just a few nops to finish out the delay. It's more effort to write but much more compact.Flews
..If the delay isn't that long, try a macro for say 5 nops to save typing, but I don't think theres a way to make a "paste this N times" macro as you've requested.Flews
You can make n macros though, where n is the max number of repeats you may need. Something like #define _5(x) x ## _4(x) where _1(x) ... _4(x) would look similarMcwhirter
If you're willing to #include a file recursively, you can cook something up...Nobby
Why are you repeating code rather than using a for loop or calling a delay function?Cardiology
Many assembly languages have macros for repeating blocks of code. You may want to write a delay in assembly code.Cardiology
can't you use something like this: #define print(X) for(int i = 0 ;i<=X;++i) printf("hello")Mabel
Have a look at #319828 for generating loops using macrosDecameter
F
13

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

Farias answered 19/3, 2015 at 12:1 Comment(1)
+1 for the C++ non-preprocessor solution. Exactly one of those edge cases where unrolling the loop at compile-time using template programming is extremely useful.Refugiorefulgence
D
5

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.
Disarticulate answered 11/8, 2017 at 8:18 Comment(1)
Why is this getting upvotes, it's janky and doesn't really do the job, not programmatically at least.Crespi
T
2

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);
Tega answered 20/11, 2014 at 17:21 Comment(0)
R
1

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

Referent answered 18/8, 2020 at 12:47 Comment(0)
R
1

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

Riboflavin answered 8/11, 2023 at 22:50 Comment(1)
This one's actually pretty clever, I like it!Crespi

© 2022 - 2024 — McMap. All rights reserved.