从 C 到汇编

电器工程 avr C 集会
2022-01-21 12:34:14

假设我们有以下一段用于 avr-8bit 的 C 代码:

int v1=1;
int v2=2;
v2=v2+v1;

我预计以下反汇编

ldi r18, 1;
ldi r19, 2;
add r19, r18;

但在我跑之后:

avr-gcc -mmcu=atmega2560 Test.c -o Test.elf

avr-objdump -S Test.elf > Test.lss

我得到了以下反汇编

    ldi r24, 0x01   ; 1
    ldi r25, 0x00   ; 0
    std Y+2, r25    ; 0x02
    std Y+1, r24    ; 0x01
    ldi r24, 0x02   ; 2
    ldi r25, 0x00   ; 0
    std Y+4, r25    ; 0x04
    std Y+3, r24    ; 0x03
    ldd r18, Y+3    ; 0x03
    ldd r19, Y+4    ; 0x04
    ldd r24, Y+1    ; 0x01
    ldd r25, Y+2    ; 0x02
    add r24, r18
    adc r25, r19
    std Y+4, r25    ; 0x04
    std Y+3, r24    ; 0x03

有没有人可以帮助我理解反汇编程序的结果?

编辑:使用 char 程序集变为:

ldi r24, 0x01
std Y+1, r24
ldi r24, 0x02
std Y+2, r24
ldd r25, Y+2
ldd r24, Y+1
add r24, r25
std Y+2, r24

什么时候有std指令?

3个回答

简短的回答:你的寄存器是 8 位的,你的值是 16 位的。因此,它将它们分成两部分处理。

长答案:

    ldi r24, 0x01   ; 1
    ldi r25, 0x00   ; 0

将 16 位值 1 存储在 8 位寄存器 r24,r25 中。

    std Y+2, r25    ; 0x02
    std Y+1, r24    ; 0x01

将其存储在堆栈位置 Y+1、Y+2。

    ldi r24, 0x02   ; 2
    ldi r25, 0x00   ; 0

将 16 位值 2 存储在 8 位寄存器 r24,r25 中。

    std Y+4, r25    ; 0x04
    std Y+3, r24    ; 0x03

将其存储在堆栈位置 Y+3、Y+4。

    ldd r18, Y+3    ; 0x03
    ldd r19, Y+4    ; 0x04
    ldd r24, Y+1    ; 0x01
    ldd r25, Y+2    ; 0x02

将它们从堆栈中复制回 (r18,r19) 和 (r24,r25)

    add r24, r18
    adc r25, r19

将 (r18,r19) 与 (r24,r25) 相加,包括第二次加法的进位

    std Y+4, r25    ; 0x04
    std Y+3, r24    ; 0x03

将其存储回堆栈中。

要获得原始程序集,请尝试两件事:

  • 使用“char”变量
  • 使用“-O2”编译器选项

编辑:编译器将变量存储到堆​​栈而不是将它们保存在寄存器中的原因是因为它们以默认的“自动”存储类型存储。可以将它们优化为寄存器,但它不必这样做,即使您使用“寄存器”存储类声明它们。

虽然这不是语言的严格要求,但它是正常的编译器行为。如果在某个时候您获取 v1 的地址,则必须为其分配一个存储位置,并在“v1”的值更改时将其保存回该位置。因此,为了节省 v1 是否应该存储在寄存器中或堆栈中的簿记,它将其保留在堆栈中并单独处理每一行代码。

当我找到一些示例代码时,我会将我的评论作为答案——其他人已经解释了这个问题。

我使用的许多嵌入式代码定义了特定于其大小的额外类型,例如无符号整数的“uint8、uint16、uint32”。这样,您始终可以准确地知道您正在处理哪种变量。尤其是在小型嵌入式、有符号、浮点、未定义大小/符号的“int”中,充其量只会花费您的 CPU 周期,最坏的情况会导致严重的错误。

这是我们当前的#defines:

/*
 * Example - the basic data types from our embedded code
 */
typedef unsigned char       uint8;  /*  8 bits */
typedef unsigned short int  uint16; /* 16 bits */
typedef unsigned long int   uint32; /* 32 bits */

typedef char                int8;   /*  8 bits */
typedef short int           int16;  /* 16 bits */
typedef int                 int32;  /* 32 bits */

typedef volatile int8       vint8;  /*  8 bits */
typedef volatile int16      vint16; /* 16 bits */
typedef volatile int32      vint32; /* 32 bits */

typedef volatile uint8      vuint8;  /*  8 bits */
typedef volatile uint16     vuint16; /* 16 bits */
typedef volatile uint32     vuint32; /* 32 bits */

您的 C 代码使用 16 位整数变量 (int)。编译器无法读懂您的想法,因此它会准确编译源文件中的内容。所以,如果你想要 8 位变量,你必须使用各自的类型。

结果,您仍然可以将值存储在内存中(尽管更简单)。我在 C 语言中不太好,但恕我直言,如果您希望某些变量位于寄存器中而不是 RAM 中,则有一些选项可以将变量分配给某个寄存器。就像是:

register unsigned char VARNAME asm("r3");

请注意,并非所有寄存器都可用于此类技巧。

那么,结论呢?在汇编中编写程序。它们总是更小、更快、更容易阅读/支持。