如何使用ARM汇编代码除以8?

时间:2019-01-13 16:14:52

标签: assembly arm divide thumb

我必须完成下面的程序框架,才能仅计算可分割数据区域中这些值的总和 乘以8,并将结果存储在寄存器r5中。我的程序必须能够处理任意数量的数据值 (在内存和32位整数范围限制内),并且用户必须只能更改值 在数据区域中,程序将继续正常运行。

    THUMB
    AREA RESET, DATA, READONLY
    EXPORT Vectors
    EXPORT Reset_Handler
Vectors
    DCD 0x20001000 ; top of the stack
    DCD Reset_Handler ; reset vector - where the program starts
    AREA Task2Code, CODE, READONLY
    ENTRY
Reset_Handler
num EQU 51
    MOV r1,#0
    MOV r5,#0
    LDR r8,=sum_up
loop
    MOV r2, r1, ROR #3 ; divide by 8
    LDR r2,[r8],#4
    ADD r5,r5,r2
    ADD r1,r1,#1
    CMP r1,#num
    B loop
terminate
    B terminate
sum_up
    DCD -16,100,-456,7,-123,-42,126789,2349,-34,-2344,45,-45,-3345 ; example values
    END

这是我到目前为止编写的代码,但是我无法运行它,也不知道如何正确地除以8,有人对此有任何想法吗?

2 个答案:

答案 0 :(得分:4)

@Peter Cordes的answer对于使用班次划分的方式是全面而准确的。但是,要完成您的任务(“仅计算数据区域中被8整除的那些值的总和”),您实际上不需要将除以8。您只需要测试是否或不是数字可以被8整除。

您可能已经知道,任何可被8整除的数字的最低有效三位在其二进制表示形式中将为零,就像任何可被1000整除的数字的后三位数字均为十进制的零一样。幸运的是,它包括二进制的负数-仅是2的补码算术的加法。因此,您的任务归结为测试每个数字的最低有效三位是否全部为零。如果是,则该数字可被8除;如果不是,那么不是。

您要实现此目的的指令是TST。该指令有效地对其自变量执行按位与运算,并根据结果设置状态标志,然后丢弃结果本身。就像没有目的地寄存器的ANDS一样。通过使用值为0x7(二进制111)的TST作为一个参数,如果另一个操作数可被8整除,则结果将为零,否则为非零;因此,如果另一个操作数可被8整除,则将置零标志,否则将其清除。

例如:

loop
    ; (somehow obtain the next number in r4 here)
    TST r4, #0x7
    BNE loop        ; If the value in r4 is not divisible by 8, continue
    ADD r5, r5, r4  ; Add the value in r4 to the sum in r5
    B loop

该示例显然是不完整的(没有将值加载到r4中,并且最终分支是无条件的),但希望它将为您提供一个起点。

答案 1 :(得分:2)

旋转显然是不正确的,它将使低位移动到符号位位置。

您想右移,例如MOV r2, r1, ASR #3,又称asr r2, r1, #3

还是您需要带符号的整数除法与C的舍入为零的行为相同?? (算术右移向-Infinity舍入,但是C风格的整数除法向零截断。)

如果是这样,可以使用符号位作为更正,以与编译器相同的方式实现。 Divide a signed integer by a power of 2。另请参见此x86示例Why does the compiler generate a right-shift by 31 bits when dividing by 2?

或者只是look at ARM compiler output on the Godbolt compiler explorer

int div8(int a) { return a/8; }

gcc -O3 -mcpu=cortex-m4(缩略图模式的asm):

div8(int):
    cmp     r0, #0
    it      lt
    addlt   r0, r0, #7        @@ increase past the next multiple of 8 if it wasn't one
    asrs    r0, r0, #3        @@ arithmetic right-shift by 3
    bx      lr

clang的代码生成更加紧凑,我认为所有16位指令都不需要it来确定其中之一:

div8(int):
    asrs    r1, r0, #31            @ copy the sign bit to all bits of r1
    add.w   r0, r0, r1, lsr #29    @ add 0 or 7 (logical shift right of all-ones or zeros)
    asrs    r0, r0, #3             @ r0 >>= 3
    bx      lr