【问题标题】:AVR Assembly: Writing to arrays without returning anythingAVR 汇编:写入数组而不返回任何内容
【发布时间】:2019-04-26 03:24:03
【问题描述】:

我必须在 AVR 程序集中编写一个程序,该程序在被 C 程序调用时接收一个指向整数数组的指针,并对其元素执行操作而不实际输出值。为简单起见,假设我希望我的程序将每个元素的值加倍 - 这样给定数组 {2, 4, 6, 8},在 C 中调用打印方法与我编写的内容分开将打印 {4, 8, 12, 16}

我的问题是我不明白如何更改数组元素的值,并在函数执行完成后让这些更改持续存在。我无法通过寄存器r24 返回任何内容,因为我需要为不同的目的返回不同的数字。

我的想法是,由于寄存器r24 上的输入作为指向数组第一个元素的指针,我将mov r26 r24,关联(?)数组与X 指针,然后ld到另一个寄存器,这样我就可以使用 X 指针在数组中递增,如ld r18, X+

虽然我在导航数组时遇到了一点麻烦,但我不明白如何让我的更改持久化,如果这有意义的话。我的印象是我应该使用st 和/或sts 来解决这个问题,但我很难理解它们是如何工作的。我的尝试是保留一个像 Z 这样的指针与输入数组相关联,每次我准备好一个值来替换数组中的旧元素时,我都会写 st Z+, rXX,将值放在索引 Z 处,然后指向到下一个索引。这不起作用,所以我想知道:我需要做什么才能将本地寄存器的内存与提供给程序的输入的内存链接起来?

【问题讨论】:

  • I don't understand how to give my changes permanence, if that makes sense - 这听起来很奇怪。您的数组在内存中,位于顺序地址。您将指针传递给起始地址并用新值覆盖此内存的内容 - 可以比这更永久。 :) 值得指定您使用的 C 编译器,因为您需要了解编译器特定的“调用约定”。而且,如果有疑问,您总是可以用 C 而不是 asm 来实现您的函数,并查看编译器生成了什么。
  • This didn't work, so I'm left wondering - 好吧,因为这实际上几乎没有告诉我们什么,所以总是建议显示实际代码(通过复制并粘贴到问题正文中,使用Edit)因为你听起来像一个很少体验可能有几十个原因导致某些事情对你不起作用,没有意义的猜测。还请指定您正在编程的确切芯片(或者如果您使用的是仿真器)。 since the input on register r24 - 我对 AVR 没有经验,但指针是 16 位的,不是吗?不能通过 r24

标签: arrays assembly ld avr cpu-registers


【解决方案1】:

首先我鼓励您阅读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

如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 寄存器),则应保留并在返回之前恢复它们之前的值。另外建议保留r0(见appnote中的表5-1,但要考虑到有错字:r0 at second from the bottom line, above r31, should be read as r30

寄存器 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 等。

【讨论】:

    猜你喜欢
    • 2023-03-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-27
    • 1970-01-01
    • 2010-11-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多