我在使用汇编语言时遇到了一些困难,不幸的是,当我在Google上搜索信息时,找不到任何可以帮助我解决问题的信息。我已经编写了这段代码,我正在寻求帮助,以查看是否有一种方法可以使它更简单(如果可能)。另外,如果评论有误,请让我知道。
NAME main
PUBLIC main
SECTION .text: CODE (2)
THUMB
main
LDR R4, =0x0097 ; R4 = 97 in hex
BL SUBROUTINE ; Go to Subroutine
STOP B STOP
SUBROUTINE
MOV R1, #1 ; Initialize R1 to 1
MOV R2, #0 ; Initialize R2 to 0
MOV R0, #0 ; Initialize R0 to 0
PUSH {R4}
LOOP
CMP R0, #8 ; Bits counter
BEQ DONE ; Go to DONE R0 = 8
ADD R0, R0, #1 ; Calculates the bits
AND R3, R4, R1 ; Checks if R3 = R4
CMP R3, #1 ; Comparing result with 1
BEQ ONE ; Jump to ONE
LSR R4, R4, #1 ; Right shift by 1
B LOOP
ONE
ADD R6, R6, #1 ; Saving #1 in R6
LSR R4, R4, #1 ; Right shift by 1
B LOOP
RETURN0
MOV R2, #0
POP {R4}
B STOP
RETURN1
MOV R2, #1
POP {R4}
B STOP
DONE
CMP R6, #2
BEQ RETURN0
CMP R6, #4
BEQ RETURN0
CMP R6, #6
BEQ RETURN0
CMP R8, #8
BEQ RETURN0
B RETURN1
END
任务如下:该子例程在寄存器R4中具有输入参数,并提供返回值寄存器R2中的值。该子例程将检查8个最小的奇偶校验输入参数的有效位。如果奇偶校验为偶数,则值为0为返回,如果奇偶校验为奇数,则返回值1。均等意味着一的数目是偶数,一的数目是奇数奇偶校验很奇怪。
提前感谢
您的编程风格已经非常不错,您可以对代码进行彻底注释。这非常有价值,您应该继续做。该算法本身看似正确,并且可以接受,但是可以更有效地实现。
编写高效的汇编代码时,最重要的事情是了解您要为其编程的体系结构的指令集。您的代码是为ARM编写的,具有许多有用的指令和功能以加快处理速度。让我们从一些基本改进入手。
首先,您使用此序列隔离R4
的最低有效位,然后检查其是否为非零:
ADD R0, R0, #1 ; Calculates the bits
AND R3, R4, R1 ; Checks if R3 = R4
CMP R3, #1 ; Comparing result with 1
BEQ ONE ; Jump to ONE
这可以更有效地完成。首先,请注意,可以将立即数与AND
指令一起使用,因此无需为此在寄存器中保留1:
AND R3, R4, #1
下一个,而不是将按位AND
的结果与#1
进行比较,您可以告诉处理器直接根据AND
指令的结果设置标志。如果结果为零,则将其设置为零(可能还有其他标志,不要太在意),因此您可以立即跳转到结果。
ANDS R3, R4, #1 ; check if least significant bit set in R4
BNE ONE ; jump to ONE if it is
现在,此ANDS
执行作业,但是不必要地将其结果写入R3
。我们真的不需要那里。快速浏览指令集引用会告诉我们TST
与ANDS
的作用相同,但是只设置标志就丢弃了结果。这正是我们想要的。
TST R4, #1 ; check if least signficant bit set in R4
BNE ONE ; jump to ONE if it is
现在,我们接下来要做的就是摆脱该条件分支。 ONE
分支中代码之间的唯一区别是,它递增R6
。代替条件分支,我们仅在设置了零标志时才可以使用ARM的条件执行功能执行ADD
指令:
TST R4, #1 ; check if least significant bit set in R4
ADDNE R6, R6, #1 ; increment R6 if it is
这使代码效率更高!通过将TST
合并到LSR
指令中,我们甚至可以进一步改进。看,如果我们告诉LSR
设置标志,它将进位标志设置为移出的最后一位。这正是我们感兴趣的!这样我们就可以做到
LSRS R4, R4, #1 ; shift R4 to the right and set flags
ADDCS R6, R6, #1 ; increment R6 if a 1 was shifted out
请注意,在其他没有条件执行的体系结构上,您可以使用带进位加法指令达到与ADDCS R6, R6, #1
类似的效果:
ADC R6, R6, #0 ; add 1 to R6 if carry is set
除了设置进位标志之外,如果结果为零,LSRS
还将设置零标志。因此,如果我们简单地迭代直到R4
中的所有位都被移出,则可以省去循环计数器,从而为我们节省了寄存器和一堆指令。请注意,如果在R4
中设置了任何额外的位(除了检查的最低8位之外),这可能不会产生正确的结果,因此您可能想先用AND R4, R4, #0xff
屏蔽掉它们。这是代码:
LOOP: LSRS R4, R4, #1 ; shift R4 to the right and set flags
ADDCS R6, R6, #1 ; increment R6 if a 1 was shifted out
BNE LOOP ; loop until R4 is 0.
您可以类似地优化DONE
部分中的代码:本质上,您只需检查R6
是偶数还是奇数,然后返回1
如果是奇数或0
如果是偶数。您可以用一个测试来代替整个跳跃过程:
TST R6, #1 ; set the zero flag if R6 is even
BEQ RETURN0 ; return 0 if even
B RETURN1 ; otherwise return 1
但是,请注意,这基本上与返回R6
的最低有效位相同,因此您可以通过以下方式替换整个代码:>
AND R0, R6, #1 ; set R0 to 1 if R6 is odd, 0 if R6 is even POP {R4} B STOP
这有点短,不是吗?
现在需要进行一些算法上的改进:您的代码可以解决问题,但确实很慢,因为每个循环执行一次迭代。一种更快的方法是使用XOR
指令将位压缩在一起。这样一来,我们只需3个步骤即可计算奇偶校验,而不是像您的代码那样计算8个奇偶校验:
LSR R3, R6, #4 ; keep a copy of R6 shifted by 4 places EOR R6, R6, R3 ; and xor it into R6 LSR R3, R6, #2 EOR R6, R6, R3 ; same but shifted by 2 places LSR R3, R6, #1 EOR R6, R6, R3 ; same but shifted by 1 place AND R0, R6, #1 ; isolate parity
这可以使用移位的操作数进一步改进,这是ARM的另一个特定功能:
EOR R6, R6, R6, LSR #4 ; xor R6 with R6 shifted right 4 places EOR R6, R6, R6, LSR #2 ; xor R6 with R6 shifted right 2 places EOR R6, R6, R6, LSR #1 ; xor R6 with R6 shifted right 1 place AND R0, R6, #1 ; isolate parity
这是通常不使用任何指令集扩展的最快方法。如果您有足够先进的处理器,则可以使用
CNT
指令一步计算位数,但是,这在这里还是不值得的。
下一次使用CODE(编辑器中的花括号)代替打印屏幕(例如,您无法从prtscn复制粘贴)。我从未使用过ARM汇编语言,但会使用这种方法: