这是我正在开发的一个 C 项目中的反汇编 AVR 代码片段。我注意到生成了这个奇怪的代码,但我无法理解它是如何工作的。我假设这是某种荒谬的优化......
解释是什么?
92: ticks++; // unsigned char ticks;
+0000009F: 91900104 LDS R25,0x0104 Load direct from data space
+000000A1: 5F9F SUBI R25,0xFF Subtract immediate
+000000A2: 93900104 STS 0x0104,R25 Store direct to data space
95: if (ticks == 0) {
+000000A4: 2399 TST R25 Test for Zero or Minus
+000000A5: F009 BREQ PC+0x02 Branch if equal
+000000A6: C067 RJMP PC+0x0068 Relative jump
具体来说,为什么第二条指令从 R25 中减去 0xFF 而不是仅仅
INC R25
?
SUBI 指令可用于将任何 8 位常数与 8 位值相加或相减。它与 INC 具有相同的成本,即指令大小和执行时间。所以SUBI是编译器首选的,因为它更通用。没有相应的ADDI指令,可能是因为它是多余的。
tl;dr 编译器被设计为使用更便携、高效和通用的解决方案。
SUBI
指令集C
(进位)和H
(半进位)CPU标志用于后续指令(8位AVR BTW中没有ADDI
,因此要添加立即数x
的值我们减去 -x
它),而 INC
则不然。由于 SUBI
和 INC
都有 2 个字节的长度并在 1 个时钟周期内执行,因此使用 SUBI
不会丢失任何内容 - OTOH,如果您使用 8 位大小的计数器,您可以轻松检测它是否已翻滚(通过 BRCC
/BRCS
),并且如果你有一个 16 位或 32 位大小的计数器,它允许你以一种非常简单的方式递增它 - 只需 INC
,0x00FF
就会增加到 0x0000
,所以你必须在 0xFF
之前检查最低字节是否为 INC
。 OTOH,使用 SUBI
,您只需 SUBI -1
最低字节,然后 ADC 0
表示以下字节,确保所有潜在的进位位都已被考虑在内。
进一步阅读:
https://lists.gnu.org/archive/html/avr-gcc-list/2008-11/msg00029.html
http://avr-gcc-list.nongnu.narkive.com/SMMzdBkW/foo-subi-vs-inc