为什么这段代码是由 avr-gcc 生成的?它是如何工作的?
这是我正在开发的一个 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
?
This is a snippet of disassembled AVR code from a C project I'm working on. I noticed this curious code being generated, and I can't understand how it works. I'm assuming it's some sort of ridiculous optimization...
What is the explanation?
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
Specifically, why does the second instruction subtract 0xFF from R25 instead of just INC R25
?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
SUBI 指令可用于将任何 8 位常数与 8 位值相加或相减。它与 INC 具有相同的成本,即指令大小和执行时间。所以SUBI是编译器首选的,因为它更通用。没有相应的 ADDI 指令,可能是因为它是多余的。
The SUBI instruction can be used to add/subtract any 8 bit constant to/from an 8 bit value. It has the same cost as INC, i.e. instruction size and execution time. So SUBI is preferred by the compiler because it is more general. There is no corresponding ADDI instruction, probably because it would be redundant.
tl;dr 编译器被设计为使用更可移植、更高效的&一般解决方案在这里。
SUBI
指令集C
(进位)和H
(半进位)CPU 标志用于后续指令(顺便说一句,8 位 AVR 中没有ADDI
,因此添加立即数x
我们从中减去-x
),而INC
没有。由于SUBI
和INC
有 2 个字节的长度并在 1 个时钟周期内执行,使用SUBI
你不会丢失任何东西 - OTOH,如果你使用 8 位大小的计数器,你可以轻松检测它是否已翻转(通过BRCC
< /a>/BRCS
),如果您有一个 16 位或 32 位大小的计数器,它允许您以非常简单的方式递增它 - 只需INC
,0x00FF
就会得到增加到0x0000
,因此在INC
之前必须检查最低字节是否为0xFF
。 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
tl;dr the compiler was designed to use the more portable, efficient & general solution here.
The
SUBI
instruction setsC
(carry) andH
(half-carry) CPU flags for use with subsequent instructions (there is noADDI
in 8-bit AVR BTW, so to add an immediate value ofx
we subtract-x
from it), whereasINC
does not. Since bothSUBI
&INC
have 2 bytes of length and execute during 1 clock cycle, you lose nothing by usingSUBI
- OTOH, if you use a 8-bit-sized counter, you can then easily detect if it has rolled over (byBRCC
/BRCS
), and if you'd have a 16- or 32-bit-sized counter, it allows you to increment it in a very simple way - with justINC
,0x00FF
would get increased to0x0000
, so you'd have to check if the lowest byte is0xFF
beforeINC
ing. OTOH, withSUBI
you justSUBI -1
the lowest byte, and thenADC 0
for the following bytes, assuring all the potential carry bits has been accounted for.Further reading:
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