Pages: [1]   Go Down
Author Topic: Can you get at the return address from a C function?  (Read 677 times)
0 Members and 1 Guest are viewing this topic.
Manchester (England England)
Online Online
Brattain Member
*****
Karma: 626
Posts: 34153
Solder is electric glue
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

While doing this tutorial http://www.thebox.myzen.co.uk/Tutorial/Merging_Code.html it struck me that a sort of simple cooperative task swapping routine could be written to replace delay().
What would happen is that when called it would look at a list of functions that had called it and return to a function only when the passed delay value had timed out. Therefore you could have say two blinking light routines each calling this delayAndSwap() function and the two functions would do a sort of task swapping.

What is needed is access to the function return stack. It is easy enough in machine code but is there a way of getting this in C?
Logged

Offline Offline
Edison Member
*
Karma: 48
Posts: 1631
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

AFAIK that can't be done. But could you still implement it by having a calling routine pass you the address of a callback function?

Pete
Logged

West Des Moines, Iowa USA
Offline Offline
Sr. Member
****
Karma: 2
Posts: 428
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

The calling routine can only pass the address of an entry point, so you can call a designated function. I have a version of delay that does what GM would like to do, but only one task at a time can call it (which makes it not very useful).

Here's what that code looks like:

Code:
/*----------------------------------------------------------------------------*/
/* paus() - pause execution of a function while allowing others to run        */
/*----------------------------------------------------------------------------*/
void paus(time_t dlay)
{  time_t then = now() + dlay;         /* Calculate time to resume execution  */
   while (now() < then) idle();        /* Until then, run other programs      */
}                                      /*  end: paus()                        */
/*----------------------------------------------------------------------------*/
Logged

There's always a better way!

0
Offline Offline
Shannon Member
****
Karma: 206
Posts: 12179
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

While doing this tutorial http://www.thebox.myzen.co.uk/Tutorial/Merging_Code.html it struck me that a sort of simple cooperative task swapping routine could be written to replace delay().
What would happen is that when called it would look at a list of functions that had called it and return to a function only when the passed delay value had timed out. Therefore you could have say two blinking light routines each calling this delayAndSwap() function and the two functions would do a sort of task swapping.

What is needed is access to the function return stack. It is easy enough in machine code but is there a way of getting this in C?

You need multiple stacks to swap in this way (via a call - known as coroutines).  Light-weight-thread basically means a stack, and can be cooperative (coroutines) or pre-emptive (switched on timer interrupts - more problematical).  setjmp() and longjmp() can handle the return address to some extent IIRC, but you need a library to manage the stacks.  In general the compiler has to understand what is going on too (setjmp/longjmp semantics should be understood by the compiler).

Stack switching itself is easy - save all registers to the current stack, switch stack pointer, then restore registers from the new stack and return.  Somewhere you have to record information about which stack(s) to switch to when next switching, but this is usually a parameter of the coroutine call.
http://en.wikipedia.org/wiki/Coroutine#Implementations_for_C
So yes, you need to manipulate the return values, but also the stack pointer, and need to set-aside space for each thread's stack
Logged

[ I won't respond to messages, use the forum please ]

Manchester (England England)
Online Online
Brattain Member
*****
Karma: 626
Posts: 34153
Solder is electric glue
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Yes I know all that. I can do it in machine code. I first did it in machine code back in 1978. However, if I am to do it in C I need access to the C stack where the return address is stored but it looks like I can't get it.
Logged

Global Moderator
UK
Offline Offline
Brattain Member
*****
Karma: 302
Posts: 26293
I don't think you connected the grounds, Dave.
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

It used to be a question of taking the address of the first local variable and working back, but in these days of optimisers, I don't see that working.
Logged

"Pete, it's a fool looks for logic in the chambers of the human heart." Ulysses Everett McGill.
Do not send technical questions via personal messaging - they will be ignored.

Offline Offline
God Member
*****
Karma: 32
Posts: 507
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

This reminded me of a piece of code I wrote a few months ago. I wanted to write a function: boolean every(long micros) , that would return true if micros microseconds had elapsed since last time it returned true, and false at all other times. So you could write for example: if(every(1000000))toggleLed(13); To discriminate between different calls to every in the same program I needed to know the return address to the caller, and I used __builtin_return_address(0) for that. There is  __builtin_frame_address too which might be helpful.

Here are the docs for those functions: http://gcc.gnu.org/onlinedocs/gcc/Return-Address.html

(What I ended up doing with every was to use preprocessor metaprogramming to make every into a macro. Then I could use __COUNTER__ to discriminate between calls. Usage is now even simpler: every(1000000)toggleLed(13); This is probably the worst hack I have ever written, but for simple LED flashing it seems to work)

Code:
//usage: every(time) doSomething();
// time = period in microseconds
// Be careful, this macro has a trailing if and can bite you. You have been warned!
// This code guaranteed safe for use in life support systems, nuclear power stations
// and large hadron colliders

#define CONCAT( a, b ) a##b
#define every2(id,time) static unsigned long CONCAT(_ec , id) = 0;                               \
                        unsigned long CONCAT(_et , id) = micros();                               \
                        boolean CONCAT(_eb , id) = (CONCAT(_et , id) -CONCAT(_ec , id)) >= time; \
                        if(CONCAT(_eb , id)) CONCAT(_ec , id) += time;                           \
                        if(CONCAT(_eb , id))              
#define every(time) every2( __COUNTER__ ,time)

boolean p2,p3,p4,p5,p6,p7,p8,p9,p10,p11,p12,p13;

void setup() {
  for(int i=2;i<=13;i++)pinMode(i,OUTPUT);
}

void loop() {  
  every(30000000/51) digitalWrite( 2, p2= !p2);
  every(30000000/52) digitalWrite( 3, p3= !p3);
  every(30000000/53) digitalWrite( 4, p4= !p4);
  every(30000000/54) digitalWrite( 5, p5= !p5);
  every(30000000/55) digitalWrite( 6, p6= !p6);
  every(30000000/56) digitalWrite( 7, p7= !p7);
  every(30000000/57) digitalWrite( 8, p8= !p8);
  every(30000000/58) digitalWrite( 9, p9= !p9);
  every(30000000/59) digitalWrite(10,p10=!p10);
  every(30000000/60) digitalWrite(11,p11=!p11);
  every(30000000/61) digitalWrite(12,p12=!p12);
  every(30000000/62) digitalWrite(13,p13=!p13);
}
« Last Edit: March 05, 2012, 02:49:35 pm by stimmer » Logged


West Des Moines, Iowa USA
Offline Offline
Sr. Member
****
Karma: 2
Posts: 428
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
// This code guaranteed safe for use in life support systems, nuclear power stations
// and large hadron colliders

For my nuclear power station I decided to simply extend micros() to 64 bits and eat the memory/time overhead:

Code:
/*----------------------------------------------------------------------------*/
/* Extend micros() for 64-bit up-time                                      TU */
/*----------------------------------------------------------------------------*/
static time_t u64(void)
{  static union                        /* Definitions to allow accessing time */
   {  time_t hl;                       /* As a whole, or as...                */
      struct                           /* Anonymous struct container          */
      {  unsigned long l;              /* Lower half and...                   */
         unsigned long h;              /* Upper half                          */
      };                               /*  end: (anonymous) {}                */
   }  u = { 0 };                       /* Initialize to zero                  */
   static unsigned long t;             /* Variable to hold micros() value     */
   if (u.l > (t = micros())) ++u.h;    /* If micros() wrapped, adjust         */
   u.l = t;                            /* Save updated low half               */
   return u.hl;                        /* Return 64-bit usec time             */
}                                      /*  end: u64()                         */

In this application u64() is called at least once every second, so there's no danger of missing an overflow.
Logged

There's always a better way!

nr Bundaberg, Australia
Offline Offline
Tesla Member
***
Karma: 127
Posts: 8517
Scattered showers my arse -- Noah, 2348BC.
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Can you use NAKED on a normal function call. I've used it in ISRs before and implemented my own preamble so I knew exactly where the regs where.

______
Rob
Logged

Rob Gray aka the GRAYnomad www.robgray.com

Pages: [1]   Go Up
Jump to: