我正在尝试为arduino编写一个程序,它可以可变地计算绝对地址,然后跳转到它(这是具有更大用途的更大程序的一部分),但是我遇到了一些麻烦。目前,我正在做类似以下的事情:
uint16_t v_pc;
uint16_t jmpAddress;
jmpAddress = 0x00cf;
v_pc = 0x01;
jmpAddress = calc_address(jmpAddress, v_pc);
asm("jmp %0" :: "r" (jmpAddress));
其中 calc_address 是:
uint16_t calc_address(uint16_t jmpAddress, uint16_t v_pc){
return jmpAddress += v_pc;
}
但是,当我尝试链接该程序(即 avr-gcc -mmcu=atmega328p program.o -o program)时,出现以下错误:
对“r24”的未定义引用 collect2:错误:ld 返回 1 退出状态
是否有更好的方法可以实现此目的,或者有人可以描述为什么会发生此错误?
谢谢!
JMP
不将寄存器作为操作数。对于间接跳转,您可以使用 IJMP
但隐式使用 Z
寄存器。因此,您的内联汇编应该看起来更像:
asm("ijmp" :: "z" (jmpAddress));
有两种情况需要考虑:程序内存为 128 KiB 或更少的设备,以及程序内存更大的设备。
一开始就不需要内联汇编。 您只需计算字地址并通过该指针调用该函数即可。 从语法上讲,最简单的方法是
typedef
一个相应的函数指针类型,如下所示 func_t
:
typedef void (*func_t)(void);
extern func_t calc_address (uint16_t, uint16_t);
void my_code (void)
{
// Compute word address.
func_t pfunc = calc_address (0x00cf, 0x01);
// Indirect call to computed word address.
pfunc ();
// Call word address 0x1234 (byte address 0x2468).
pfunc = (func_t) 0x1234;
pfunc ();
}
AVR 的 GNU 工具中的函数指针是 16 位宽,因此它们最多可以定位 64 KiWords = 128 KiB 的范围。 在较大的设备上,这些工具将使用所谓的 calles 链接器存根来促进超出 128 KiB 边界的间接调用。 请参阅 avr-gcc:
EIND
以及具有超过 128 Ki 字节闪存的设备。
当
calc_address
只是返回某些标签的地址时,编译器将使用 gs()
修饰符计算它们的地址。 这将根据需要弹出链接器存根,并且可以像 <= 128 KiB case. 中一样使用 16 位地址
这更复杂,因为
calc_address
不会返回某个标签的地址,而是返回任意代码位置。 这意味着您必须使用宽度超过 16 位的实体来保存地址。 并且您必须使用内联汇编来跳转到该地址。 请注意,EIND
寄存器没有任何帮助,因为您可以跳转到任何位置,但无法在被调用者中重置EIND
。 因此下面的代码使用RET
来完成间接跳转:
typedef uint32_t xfunc_t;
void my_code (void)
{
// Word address 0x12345 (byte address 0x2468a).
xfunc_t pfunc = 0x12345;
// Indirect jump to address.
__asm volatile ("push %A0" "\n\t"
"push %B0" "\n\t"
"push %C0" "\n\t"
"ret" :: "r" (pfunc) : "memory");
}