user65174: Due to some curiosity, I was trying to use some assembly with my Arduino MEGA 2560.
I am not able to invoke a function from a c++ function pointer array.
When I uncomment the call function, func_a runs. However, when I almost reproduce the assembly, it doesnt work.
Example:
extern "C" void __attribute__ ((used, noinline, noreturn)) func_a();

typedef void (*volatile func_ptr)();

func_ptr tasks[MAX_TASKS] = {&func_a};

extern "C"
void __attribute__ ((used, noinline)) call(func_ptr *ptr) {
    (*ptr)();
}

void __attribute__ ((noreturn, used)) setup() {
    
    // call(tasks);

    asm volatile (
    "lds r26, (tasks)\n"
    "lds r27, (tasks + 1)\n"

    "ld r30, X+\n"
    "ld r31, X\n"

    "eijmp\n"
    );
}

Generated assembly recovered from avr-objdump
000001c6 <setup>:
 1c6:   a0 91 00 02     lds r26, 0x0200 ; 0x800200 <tasks>
 1ca:   b0 91 01 02     lds r27, 0x0201 ; 0x800201 <tasks+0x1>
 1ce:   ed 91           ld  r30, X+
 1d0:   fc 91           ld  r31, X
 1d2:   19 94           eijmp
 1d4:   08 95           ret

000001d6 <call>:
 1d6:   dc 01           movw    r26, r24
 1d8:   ed 91           ld  r30, X+
 1da:   fc 91           ld  r31, X
 1dc:   19 94           eijmp

An specific resources I should refer for things like this?
If it matters, I'm using platformio on a linux system for compilation.

user4143: I will start off by saying that I am no expert in AVR assembly, so I have no idea if any of this is actually correct. However from reading the various documents on the subject I have gleaned:

On the ATMega2560 EIJMP uses a combination of both the Z register and the EIND register to form a full address.

That means that as well as setting up the Z register for the lower 16 bits of the address you also have to set up the EIND register for the upper 5 bits of the address.

Indirect jump to the address pointed to by the Z (16 bits) Pointer Register in the Register File and the EIND Register in the I/O space. This instruction allows for indirect jumps to the entire 4M (words) Program memory space.

It then goes on to show an example:
ldi r16,$05 ; Set up EIND and Z-pointer 
out EIND,r16  
ldi r30,$00 
ldi r31,$10 
eijmp ; Jump to $051000

How you determine what the EIND register should be? Well, in general you don't. You wouldn't use EIJMP. From what I understand for functions outside the lower 64kwords of address space the compiler builds a "jump table" which is stored in the lower area of memory. You jump to the entry in that table with IJMP and that table then jumps to the final destination with EIJMP for you.
Doing this kind of jumping in assembly isn't really a nice thing to try and figure out. It's messy. Better to let the compiler do it for you and use C rather than assembly.  The AVR-GCC guys put a lot of work into making function pointers work correctly (from what I have read it wasn't easy even for them), and you're just re-inventing the wheel.
At the very least you should implement your code 100% in C to begin with then look at the compiled output to see how the compiler thinks it should be done.

user7508: It seems you got confused by the pointer indirections, which is
confounded by the implicit indirections made by the compiler.
Here:
extern "C"
void __attribute__ ((used, noinline)) call(func_ptr *ptr) {
    (*ptr)();
}

The parameter ptr is not a function pointer: it's a pointer to a
function pointer. You have to dereference it twice in order to get the
function: (**ptr)();. Note that the compiler implicitly dereferences a
function pointer if you call it like a function: that's why you get no
error by explicitly dereferencing the pointer only once. In the
generated assembly, the lines
ld  r30, X+
ld  r31, X

dereference ptr in order to get the address of the function to call.
You probably wanted to write
extern "C"
void __attribute__ ((used, noinline)) call(func_ptr ptr) {
    ptr();
}

And then use it as:
call(tasks[0]);

If you just call(tasks), the array identifier decays to pointer, and
the compiler handles this as a shortcut for call(&tasks[0]);, which
turns out to be compatible with your initial implementation of call().
Now, in your inline assembly you have
lds r26, (tasks)
lds r27, (tasks + 1)

The assembly treats tasks as a symbol representing the address of
the tasks array. The linker replaces those symbols by the actual
address (0x0200). Those instructions read the RAM at this and the
following address. These slots of RAM hold the first item of the array,
namely &func_a. So now you have this address in the X register pair.
No need to read RAM again: you can movw r30, r26 and do the indirect
jump. Or, better yet, load into Z to begin with:
lds r30, (tasks)
lds r31, (tasks + 1)
eijmp

See also Majenko's answer: here I am assuming EIND is somehow
correctly set up to begin with. On an Uno, you would just use ijmp
instead of eijmp and not worry about EIND.

65174: <p>Due to some curiosity, I was trying to use some assembly with my Arduino MEGA 2560.
I am not able to invoke a function from a c++ function pointer array.</p>
<p>When I uncomment the call function, <code>func_a</code> runs. However, when I almost reproduce the assembly, it doesnt work.</p>
<p>Example:</p>
<pre><code>extern &quot;C&quot; void __attribute__ ((used, noinline, noreturn)) func_a();

typedef void (*volatile func_ptr)();

func_ptr tasks[MAX_TASKS] = {&amp;func_a};

extern &quot;C&quot;
void __attribute__ ((used, noinline)) call(func_ptr *ptr) {
    (*ptr)();
}

void __attribute__ ((noreturn, used)) setup() {
    
    // call(tasks);

    asm volatile (
    &quot;lds r26, (tasks)\n&quot;
    &quot;lds r27, (tasks + 1)\n&quot;

    &quot;ld r30, X+\n&quot;
    &quot;ld r31, X\n&quot;

    &quot;eijmp\n&quot;
    );
}
</code></pre>
<p>Generated assembly recovered from avr-objdump</p>
<pre><code>000001c6 &lt;setup&gt;:
 1c6:   a0 91 00 02     lds r26, 0x0200 ; 0x800200 &lt;tasks&gt;
 1ca:   b0 91 01 02     lds r27, 0x0201 ; 0x800201 &lt;tasks+0x1&gt;
 1ce:   ed 91           ld  r30, X+
 1d0:   fc 91           ld  r31, X
 1d2:   19 94           eijmp
 1d4:   08 95           ret

000001d6 &lt;call&gt;:
 1d6:   dc 01           movw    r26, r24
 1d8:   ed 91           ld  r30, X+
 1da:   fc 91           ld  r31, X
 1dc:   19 94           eijmp
</code></pre>
<p>An specific resources I should refer for things like this?</p>
<p>If it matters, I'm using platformio on a linux system for compilation.</p>
 4143: <p>I will start off by saying that I am no expert in AVR assembly, so I have no idea if any of this is actually correct. However from reading the various <a href="http://ww1.microchip.com/downloads/en/devicedoc/atmel-0856-avr-instruction-set-manual.pdf" rel="nofollow noreferrer">documents</a> on the subject I have gleaned:</p>
<ul>
<li>On the ATMega2560 <code>EIJMP</code> uses a combination of both the Z register <em>and</em> the <code>EIND</code> register to form a full address.</li>
</ul>
<p>That means that as well as setting up the Z register for the lower 16 bits of the address you also have to set up the <code>EIND</code> register for the upper 5 bits of the address.</p>
<blockquote>
<p>Indirect jump to the address pointed to by the Z (16 bits) Pointer Register in the Register File and the EIND Register in the I/O space. This instruction allows for indirect jumps to the entire 4M (words) Program memory space.</p>
</blockquote>
<p>It then goes on to show an example:</p>
<pre><code>ldi r16,$05 ; Set up EIND and Z-pointer 
out EIND,r16  
ldi r30,$00 
ldi r31,$10 
eijmp ; Jump to $051000
</code></pre>
<p>How you determine what the EIND register should be? Well, in general you don't. You wouldn't use EIJMP. From what I understand for functions outside the lower 64kwords of address space the compiler builds a &quot;jump table&quot; which is stored in the lower area of memory. You jump to the entry in that table with <code>IJMP</code> and that table then jumps to the final destination with <code>EIJMP</code> for you.</p>
<p>Doing this kind of jumping in assembly isn't really a nice thing to try and figure out. It's messy. Better to let the compiler do it for you and use C rather than assembly.  The AVR-GCC guys put a lot of work into making function pointers work correctly (from what I have read it wasn't easy even for them), and you're just re-inventing the wheel.</p>
<p>At the very least you should implement your code 100% in C to begin with then look at the compiled output to see how the compiler thinks it should be done.</p>
 7508: <p>It seems you got confused by the pointer indirections, which is
confounded by the <em>implicit</em> indirections made by the compiler.</p>
<p>Here:</p>
<pre>extern &quot;C&quot;
void __attribute__ ((used, noinline)) call(func_ptr *ptr) {
    (*ptr)();
}
</pre>
<p>The parameter <code>ptr</code> is <em>not</em> a function pointer: it's a pointer to a
function pointer. You have to dereference it <em>twice</em> in order to get the
function: <code>(**ptr)();</code>. Note that the compiler implicitly dereferences a
function pointer if you call it like a function: that's why you get no
error by explicitly dereferencing the pointer only once. In the
generated assembly, the lines</p>
<pre class="lang-lisp prettyprint-override"><code>ld  r30, X+
ld  r31, X
</code></pre>
<p>dereference <code>ptr</code> in order to get the address of the function to call.</p>
<p>You probably wanted to write</p>
<pre>extern &quot;C&quot;
void __attribute__ ((used, noinline)) call(func_ptr ptr) {
    ptr();
}
</pre>
<p>And then use it as:</p>
<pre>call(tasks[0]);
</pre>
<p>If you just <code>call(tasks)</code>, the array identifier decays to pointer, and
the compiler handles this as a shortcut for <code>call(&amp;tasks[0]);</code>, which
turns out to be compatible with your initial implementation of <code>call()</code>.</p>
<p>Now, in your inline assembly you have</p>
<pre class="lang-lisp prettyprint-override"><code>lds r26, (tasks)
lds r27, (tasks + 1)
</code></pre>
<p>The assembly treats <code>tasks</code> as a <em>symbol</em> representing the address of
the <code>tasks</code> array. The linker replaces those symbols by the actual
address (<code>0x0200</code>). Those instructions read the RAM at this and the
following address. These slots of RAM hold the first item of the array,
namely <code>&amp;func_a</code>. So now you have this address in the X register pair.
No need to read RAM again: you can <code>movw r30, r26</code> and do the indirect
jump. Or, better yet, load into Z to begin with:</p>
<pre><code>lds r30, (tasks)
lds r31, (tasks + 1)
eijmp
</code></pre>
<p>See also Majenko's answer: here I am assuming <code>EIND</code> is somehow
correctly set up to begin with. On an Uno, you would just use <code>ijmp</code>
instead of <code>eijmp</code> and not worry about <code>EIND</code>.</p>
