AVR虚拟处理器RJMP指令C实现异常问题排查求助
解决AVR虚拟处理器RJMP指令的两个核心问题
看起来你在RJMP的汇编测试和C实现上都踩了坑,我来帮你逐一梳理修复:
1. 先修正你的汇编测试代码
你写的rjmp ldi r16, 0x00是完全错误的——AVR的RJMP指令操作数必须是标签,不能直接指定指令。你需要先给目标指令定义标签,再跳转。比如你想跳到ldi r17, 0x32,可以这么写:
.text .global main .type main, @function main: ldi r30, 0x00 mov r3, r1 mov r4, r2 rjmp target_r17 ; 跳转到预定义的标签 ldi r16, 0x00 ldi r16, 0x01 target_r17: ; 给目标指令添加标签 ldi r17, 0x32
如果想跳回main的首条指令,直接写rjmp main即可。另外你提到用$+2报错,AVR GAS汇编里$代表当前PC,但RJMP的偏移是字偏移(1个单位对应2字节),所以rjmp $是无限循环,rjmp $+1才是跳转到下一条指令(+1个单位=+2字节)。
2. 修复C语言的RJMP实现逻辑
你的F_JMP_REL函数有3个关键错误,完全不符合AVR的RJMP指令规范:
错误点梳理
- 偏移量提取错误:RJMP的偏移量是低11位有符号数,你用
0x0FFF提取了12位,多取的一位直接导致偏移计算混乱。 - PC基准值错误:AVR执行指令时,PC会先自增2(指向当前指令的下一条),再执行跳转逻辑,你直接用当前指令的PC作为基准是错的。
- 偏移量转换错误:RJMP的偏移是字偏移(1单位=2字节),你直接按字节偏移计算,导致跳转地址偏差一倍。
修正后的代码
#include <stdint.h> // 需要引入这个头文件处理有符号整数 void F_JMP_REL(void) { uint16_t current_pc = getPC(); uint16_t opcode = getOpcode(); // 提取低11位的有符号偏移量k,自动完成符号扩展 int16_t k = (int16_t)(opcode & 0x07FF); // 计算目标地址:当前PC+2(下一条指令地址) + 偏移量*2(转成字节偏移) uint16_t target_pc = current_pc + 2 + (k * 2); printf("0x%04X[0x%04X]: JMP PC+0x%04X -> 0x%04X\n", current_pc, opcode, (k * 2), target_pc); setPC(target_pc); addCounter(1); }
逻辑验证
比如你提到的测试场景:如果RJMP指令地址是0x0001,opcode低11位为0x001(k=1),那么目标地址应为0x0001 + 2 + (1*2) = 0x0005,这才是符合AVR规范的跳转结果,之前的错误逻辑就是因为偏移提取、基准PC和字节转换的三重错误,才跳到了错误的0x07FB。
内容的提问来源于stack exchange,提问作者Patryk Woźniak
相关产品推荐
相关产品推荐

