AVR 程序集:写入数组而不返回任何内容
AVR Assembly: Writing to arrays without returning anything
我必须用 AVR 程序集编写一个程序,当被 C 程序调用时,它接受一个指向整数数组的指针,并对其元素执行操作而不实际输出值。为简单起见,假设我希望我的程序将每个元素的值加倍 - 这样给定数组 {2, 4, 6, 8}
,在 C 中调用打印方法与我编写的内容分开将打印 {4, 8, 12, 16}
.
我的问题是我不明白如何改变数组元素的值,并让这些改变在函数完成执行后保持不变。我不能通过寄存器 r24
return 任何东西,因为我需要 return 一个不同的数字用于不同的目的。
我的想法是,由于寄存器 r24
上的输入作为指向数组第一个元素的指针出现,我会 mov r26 r24
,将数组与 X 指针相关联(?) ,然后 ld
到另一个寄存器,这样我就可以使用 X 指针递增数组,如 ld r18, X+
.
虽然我在数组中导航时没有什么问题,但我不明白如何让我的更改永久化,如果这有意义的话。我的印象是我应该使用 st
and/or sts
来解决这个问题,但我很难理解它们是如何工作的。我的尝试是保留一个像 Z 这样的指针与输入数组相关联,每次我有一个值准备好替换数组中的旧元素时,我会写 st Z+, rXX
,将值放在索引 Z 和随后指向下一个索引。这没有用,所以我想知道:我需要做什么来 link 我的本地寄存器的内存和提供给程序的输入的内存?
首先,我鼓励您阅读 Application Note AT1886: Mixing Assembly and C with AVRGCC(pdf 文档)
它描述了参数和 return 值如何传入和传出被调用的例程。
要使汇编代码可从 C 调用,您必须为汇编函数编写一个声明存根。你可以把它放在 .h
文件中。让它成为一个具有一个指针类型参数且没有 return 值的函数。
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
如appnote中所说,第一个参数放在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 寄存器),它们之前的值应该在 return 之前被保留和恢复。还建议保留 r0(请参阅 appnote 中的 table 5-1,但要考虑到有印刷错误:r0
at second from bottom line, above r31, should be read as r30
)
寄存器 r1 总是包含 0,如果你以某种方式改变它的值(例如调用 MUL
指令),那么你必须在 return.
之前将它清除回来
因此,考虑到我们的示例,假设您有一些 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,等等
我必须用 AVR 程序集编写一个程序,当被 C 程序调用时,它接受一个指向整数数组的指针,并对其元素执行操作而不实际输出值。为简单起见,假设我希望我的程序将每个元素的值加倍 - 这样给定数组 {2, 4, 6, 8}
,在 C 中调用打印方法与我编写的内容分开将打印 {4, 8, 12, 16}
.
我的问题是我不明白如何改变数组元素的值,并让这些改变在函数完成执行后保持不变。我不能通过寄存器 r24
return 任何东西,因为我需要 return 一个不同的数字用于不同的目的。
我的想法是,由于寄存器 r24
上的输入作为指向数组第一个元素的指针出现,我会 mov r26 r24
,将数组与 X 指针相关联(?) ,然后 ld
到另一个寄存器,这样我就可以使用 X 指针递增数组,如 ld r18, X+
.
虽然我在数组中导航时没有什么问题,但我不明白如何让我的更改永久化,如果这有意义的话。我的印象是我应该使用 st
and/or sts
来解决这个问题,但我很难理解它们是如何工作的。我的尝试是保留一个像 Z 这样的指针与输入数组相关联,每次我有一个值准备好替换数组中的旧元素时,我会写 st Z+, rXX
,将值放在索引 Z 和随后指向下一个索引。这没有用,所以我想知道:我需要做什么来 link 我的本地寄存器的内存和提供给程序的输入的内存?
首先,我鼓励您阅读 Application Note AT1886: Mixing Assembly and C with AVRGCC(pdf 文档) 它描述了参数和 return 值如何传入和传出被调用的例程。
要使汇编代码可从 C 调用,您必须为汇编函数编写一个声明存根。你可以把它放在 .h
文件中。让它成为一个具有一个指针类型参数且没有 return 值的函数。
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
如appnote中所说,第一个参数放在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 寄存器),它们之前的值应该在 return 之前被保留和恢复。还建议保留 r0(请参阅 appnote 中的 table 5-1,但要考虑到有印刷错误:r0
at second from bottom line, above r31, should be read as r30
)
寄存器 r1 总是包含 0,如果你以某种方式改变它的值(例如调用 MUL
指令),那么你必须在 return.
因此,考虑到我们的示例,假设您有一些 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,等等