我必须在AVR程序集中编写一个程序,该程序在被C程序调用时使用一个指向整数数组的指针,并对其元素执行操作,而无需实际输出值。为简单起见,假设我希望我的程序将每个元素的值加倍-这样,给定数组{2, 4, 6, 8}
,在C中调用与我编写的代码分开的print方法将打印{4, 8, 12, 16}
。>
我的问题是我不明白如何更改数组元素的值,并且在函数完成执行后使这些更改仍然存在。我无法通过寄存器r24
返回任何内容,因为我出于不同的目的需要返回不同的数字。
我的想法是,由于寄存器r24
上的输入作为指向数组第一个元素的指针进入,因此我会mov r26 r24
将数组与X指针关联(?),然后然后ld
到另一个寄存器,以便我可以使用X指针在数组中递增,就像ld r18, X+
一样。
尽管我在浏览数组时没有遇到什么麻烦,但我不理解如何赋予更改永久性(如果这样的话)。我的印象是希望使用st
和/或sts
来解决此问题,但是我一直在努力了解它们的工作原理。我的尝试是保留一个像Z这样的指针与输入数组关联,并且每当我有一个值准备替换数组中的旧元素时,我就写st Z+, rXX
,将值放在索引Z上,然后随后指向下一个索引。这没有用,所以我不禁要问:如何将本地寄存器的内存与程序提供的输入的内存链接起来?
答案 0 :(得分:2)
首先,我鼓励您阅读Application Note AT1886: Mixing Assembly and C with AVRGCC(pdf文档) 它描述了如何在调用例程之间传递参数和返回值。
要使汇编代码可从C调用,您必须为汇编函数编写一个声明存根。您可以将其放在.h
文件中。使其成为具有一个指针类型参数且没有返回值的函数。
extern void my_function(void *);
关键字extern
将告诉链接器函数主体在其他位置,而不是在此.c文件中
现在,您可以添加程序集文件,在项目中创建新的.s
文件。您可以在上面放上
#define _SFR_ASM_COMPAT 1
#define __SFR_OFFSET 0
#include <avr/io.h>
这些声明将使您能够使用指令in
/ out
/ cbi
/ sbi
等访问较低的IO寄存器。
现在,您应该声明一个与函数名称相同的标签,并声明为.extern
.extern my_function
my_function:
// assembly for the function body is here
如应用笔记中所述,第一个参数位于r25:r24中(第二个参数(如果有的话)位于r23:r22中,第三个位于r21:r20中,第四个位于r19:r18中)。如果您甚至有一个1字节的参数,则将全部使用两个寄存器,r24将存储其值,而r25则将不使用。第二个参数将在r23:r22等中。
如果您有4字节的值(例如long int
),则它将使用两个相应的参数位置,即,该值将存储在r23:r22:r25:r24
如果您的代码使用寄存器r2-r17以及r28或r29(Y寄存器),则应保留并恢复其先前的值,然后再返回。另外,建议保留r0(请参阅应用笔记中的表5-1,但要考虑到打印不正确:r0
位于r31上方第二行的底端,应读为r30
)< / p>
寄存器r1始终包含0,如果您以某种方式更改了它的值(例如,调用MUL
指令),则必须在返回之前将其清除。
因此,考虑我们的示例,假设您有一些C代码,该代码会调用汇编程序:
uint8_t my_array[10]; // declare an array
my_function(&my_array); // call the routine, passing pointer to the array
然后将调用您的函数,并且第一个参数(寄存器r25:r24)将包含指向该数组的指针。因此,您的汇编代码可以将其带入任何指针寄存器,并随心所欲。例如
.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
现在,如上例所示调用函数时,my_array[0]
将包含10,my_array[1]
== 20,等等。