【问题标题】:How to access the PC pointer (using assembly) in AVR-libc?如何访问 AVR-libc 中的 PC 指针(使用程序集)?
【发布时间】:2014-09-19 02:44:41
【问题描述】:

我正在尝试使用 AVR-gcc 在 AVR 程序集中编写一些条件跳转。根据AVR指令集手册,brxx指令接受一个操作数k,并跳转到PC+k+1。另外,根据http://www.avrbeginners.net/new/tutorials/jumps-calls-and-the-stack/ 的教程PDF,我应该可以使用PC 操作数像这样跳转:

brne PC+2

但是,当我编写这样的测试代码时:

#include <avr/io.h>
.section .text
.global main ; Note [5]
main:
    sbi _SFR_IO_ADDR(DDRA), PA0
    sbi _SFR_IO_ADDR(PORTA), PA0
    ldi 16, 0xFF
    cpi 16, 0xFF
    breq PC + 2
    cbi _SFR_IO_ADDR(PORTA), PA0
    rjmp end

end:
    rjmp end

我收到此错误:

avr-gcc -mmcu="atmega16" -DF_CPU="16000000UL" -O0 main.S -o main.o
/tmp/ccAa2ySf.o: In function `main':
(.text+0x8): undefined reference to `PC'
collect2: ld returned 1 exit status
make: *** [main.o] Error 1

显然 PC 没有在 AVR-libc 中定义。那我该怎么做这样的条件分支呢?谢谢!

更新 1

我发现了这个问题How can I jump relative to the PC using the gnu assembler for AVR?,发现gnu as 的语法是breq .+2。但是,我得到与该问题相同的错误。当我使用avr-objdump -d main.o 反汇编时,我确实得到了

  74:   01 f0           breq    .+0             ;  0x76

这与该问题的症状相同。我会尝试使用链接器脚本,但我没有这方面的经验。

更新 2

实际上我发现如果我在 breq 指令中使用偶数,比如breq .+2breq .+4,objdump 会显示正确的结果。但是,如果我使用奇数,它将变为breq .+0。谁能解释一下原因?

【问题讨论】:

    标签: gcc assembly linker branch avr


    【解决方案1】:

    好的,现在完全重写了答案。这是我从编译的 C 代码的objdump 中了解到的。首先,binutils 对程序计数器使用字节寻址,而不是字寻址,并从当前指令之后的指令开始。这在以下代码中进行了解释:

    #include <avr/io.h>
    .section .text
    .global main
    main:
        sbi _SFR_IO_ADDR(DDRA), PA0
        sbi _SFR_IO_ADDR(PORTA), PA0
        ldi 16, 0xFF
        cpi 16, 0xFF
        breq .+4  ;; If we are executing here
        cbi _SFR_IO_ADDR(PORTA), PA0  ;; This is .+0, will be skipped
        cbi _SFR_IO_ADDR(PORTA), PA0  ;; This is .+2, will be skipped
        cbi _SFR_IO_ADDR(PORTA), PA0  ;; This is .+4, which will be executed
        rjmp end
    
    end:
        rjmp end
    

    显然,PC 宽度与相对地址无关。它只影响最大 PC 值,0xFF 或 0xFFF,所以无论我为什么 AVR 平台编译,binutils 使用两个字节作为指令。

    附:我认为,如果我知道编译器如何工作的唯一方法是观察它是如何工作的,那可能意味着糟糕的文档?或者也许我只是不知道什么时候开始。如果有人看到这个,你能帮忙指点一些关于“这种事情”的有用的书吗? (我都不知道怎么形容)谢谢!

    【讨论】:

    • binutils 使用 2 个字节,因为所有指令都生成“16 位操作码”。它不是 binutils,而是 AVR 指令集规范。
    【解决方案2】:

    8 位 MCU 并不意味着汇编指令被编码为 8 位操作码。来自 ATmega16 规范。

    大多数 AVR 指令都有一个 16 位字格式。

    相反,即使 ATmega 是 8 位 MCU,所使用的指令也被编码为 16 位操作码。查看“AVR 指令集”。这就是程序计数器 (PC) 表现如此的原因(仅分配给 16 位/2 字节对齐的地址)。如果它能够设置为 8 位/1 字节对齐的地址,它将尝试执行无效的操作码!

    这是你要做的事情。将上面的示例编译为目标文件。然后反汇编文件(使用objdump -D)并查看生成的反汇编。指令的偏移量应该是 16 位对齐的。

    【讨论】:

    • 是的,我发现了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-02-16
    • 2012-08-25
    • 1970-01-01
    • 2021-07-04
    • 2011-04-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多