简体   繁体   中英

AVR Assembly: Writing to arrays without returning anything

I have to write a program in AVR assembly that takes in a pointer to an integer array when being called by a C program and performs operations on its elements without actually outputting values. For simplicity's sake, let's say I want my program to double the value of each element - Such that given the array {2, 4, 6, 8} , calling a print method in C separately from what I wrote will print {4, 8, 12, 16} .

My problem is I don't understand how to alter the values of the array's elements and have those changes persist after the function doing so has finished executing. I can't return anything through register r24 , because I need to return a different number for a different purpose.

My idea was, since the input on register r24 comes in as a pointer to the first element of the array, I would mov r26 r24 , associating(?) the array with the X pointer, and then ld that to another register so that I can use the X pointer to increment through the array, as in ld r18, X+ .

And while I have little trouble navigating the array, I don't understand how to give my changes permanence, if that makes sense. I'm under the impression that I'm expected to use st and/or sts to solve this, but I'm struggling to understand how they work. My attempt was to reserve a pointer like Z to be associated with the input array, and every time I had a value ready to replace an old element in the array, I would write st Z+, rXX , putting the value at index Z and subsequently pointing to the next index. This didn't work, so I'm left wondering: what do I need to do to link the memory of my local registers with that of the inputs provided to the program?

First I encourage you to read Application Note AT1886: Mixing Assembly and C with AVRGCC (pdf document) It describes how parameters and return values are passed to and from called routines.

To make an assembly code callable from C you have to write a declaration stub for the assembly function. You may put it in .h file. Let it be a function with one pointer-type parameter and without return values.

extern void my_function(void *); 

Keyword extern will tell the linker that the function body is somewhere else, not in this .c file

Now, you can add assembly file, creating new .s file in your project. On top of it you may put:

#define _SFR_ASM_COMPAT 1
#define __SFR_OFFSET 0

#include <avr/io.h>

Those declarations will make you able to access lower IO registers using instructions in / out / cbi / sbi etc.

Now you should declare a label which will be the same as a function name, and declare it .extern

 .extern my_function

 my_function:
   // assembly for the function body is here

As said in the appnote, first parameter is placed in r25:r24, (second, if any, in r23:r22, third in r21:r20, fourth in r19:r18). If you even have 1-byte parameter, it, all the same, will utilize two registers, r24 will store its value, while r25 will be left unused. Second parameter will be in r23:r22 etc. If you have 4-byte value ( long int , for example), then it will use two consequent parameter positions, ie it value will be stored in r23:r22:r25:r24

If your code uses registers r2-r17, also r28 or r29 (Y register), their previous values should be preserved and restored before return. Also it is recommended to preserve r0 (see table 5-1 in the appnote, but take into account there is misprint: r0 at second from bottom line, above r31, should be read as r30 )

register r1 always contains 0, if you somehow change it's value (calling MUL instruction, for example), then you have to clear it back before return.

So, considering our example, let assume you have some C code, which calls your assembler routine:

uint8_t my_array[10]; // declare an array

my_function(&my_array); // call the routine, passing pointer to the array

Then your function will be called and first parameter (registers r25:r24) will contain a pointer to the array. So, you assembly code can take it into any pointer register and do whatever you like. For example

 .extern my_function

 my_function:
   movw X, r24 // copy r25:r24 into X (r27:r26)
   ldi r18, 10
   st X+, r18 // store 10 into first element of the array
   ldi r18, 20
   st X+, r18 // store 20 into second element of the array
   ... etc

 ret // return

Now, when function is called as in example above, my_array[0] will contain 10, my_array[1] == 20, etc.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM