【ARMv7--Bit Manpulation】


ARMv7汇编指令来进行bit操作;

1 Bit Manipulation

1.1 Byte swap

在不同的计算机系统中,字节序存储可能不同。字节序指的是多字节数据在内存中的存储顺序。
主要有两种字节序: 比对对于整数:0x12_34_56_78;

  • 大端序: 高位字节存储在低地址,低位字节存储在高地址; 即12 34 56 78
  • 小端序: 低位字节存储在低地址,高位字节存储在高地址; 即78 56 34 12
    一定要记住是以字节位单位

在ARMv7中,使用汇编语言来实现32位整数字节序的交换。以下是ARMv7汇编的实现代码:

汇编代码:

// A test case to test your function with
.global _start
_start:
    ldr r0, =0x11223344
    bl bswap
    b _start        // End of testing code

// Byte swap
bswap:
    REV R0, R0	//USE REV to invert the Byte
    BX lr
	

测试代码(C语言调用汇编)

#include <stdio.h>
#include <stdint.h>

// 声明汇编函数
extern unit32_t swap_endianness(unit32_t num)

int main(){
	uint32_t num = 0x12_34_56_78;
	uint32_t swapped = swap_endianness(num);
	printf("Original: 0x%x\n", num);
	printf("Swapped: 0x%x\n",swapped);
	return 0;
}

编译与运行

  • 将汇编代码保存为 swap_endianness.s。
  • 将C代码保存为 main.c。
  • 使用交叉编译工具链(如 arm-none-eabi-gcc)编译并运行:
arm-none-eabi-gcc swap_endianness.s -mcpu=cortext-a7 -o test main.c
qemu-arm ./test

输出结果:

Origianl: 0x12_34_56_78
Swapped: 0x78_56_34_12

优点:
*高效:REV是ARMv7的专用指令,执行速度块,效率高;
*简洁:代码非常简洁,仅需要一条指令即可完成字节交换;

比较,对于不支持REV指令的ARM架构,即需要手动实现字节序交换;

swap_endianness:
    // 手动交换字节顺序
    ROR r0, r0, #24       // 将最高字节移到最低字节
    REV16 r1, r0          // 交换中间两个字节
    ROR r0, r1, #16       // 将最低字节移到最高字节
    BX lr

1.2 popcount

编写统计给入32位的整数的1的个数;
比如:5–>2; 255—>8

算法逻辑: 分治法
通过逐步合并相邻位的统计结果,最终得到整个 32 位中 1 的个数。每一步的掩码和移位操作如下:
第一步:统计每两位中的 1 的个数(掩码 0x55555555)。

第二步:统计每四位中的 1 的个数(掩码 0x33333333)。

第三步:统计每八位中的 1 的个数(掩码 0x0F0F0F0F)。

第四步:统计每十六位中的 1 的个数(掩码 0x00FF00FF)。

第五步:合并高 16 位和低 16 位的统计结果。

汇编代码


// A test case to test your function with
.global _start
_start:
    mov r0, #5
    bl popcount
    1: b 1b    // Done

// Only your function (starting at popcount) is judged. The test code above is not executed.
popcount:
    @ 第一步:每两位统计1的个数(掩码0x55555555)
    movw    r1, #0x5555          @ 加载掩码低16位
    movt    r1, #0x5555          @ 加载掩码高16位,合并为0x55555555
    and     r2, r0, r1           @ r2 = n & 0x55555555
    lsr     r3, r0, #1           @ r3 = n >> 1
    and     r3, r3, r1           @ r3 = (n >> 1) & 0x55555555
    add     r0, r2, r3           @ n = r2 + r3

    @ 第二步:每四位统计1的个数(掩码0x33333333)
    movw    r1, #0x3333
    movt    r1, #0x3333          @ r1 = 0x33333333
    and     r2, r0, r1           @ r2 = n & 0x33333333
    lsr     r3, r0, #2           @ r3 = n >> 2
    and     r3, r3, r1           @ r3 = (n >> 2) & 0x33333333
    add     r0, r2, r3           @ n = r2 + r3

    @ 第三步:每八位统计1的个数(掩码0x0F0F0F0F)
    movw    r1, #0x0F0F
    movt    r1, #0x0F0F          @ r1 = 0x0F0F0F0F
    and     r2, r0, r1           @ r2 = n & 0x0F0F0F0F
    lsr     r3, r0, #4           @ r3 = n >> 4
    and     r3, r3, r1           @ r3 = (n >> 4) & 0x0F0F0F0F
    add     r0, r2, r3           @ n = r2 + r3

    @ 第四步:每十六位统计1的个数(掩码0x00FF00FF)
    movw    r1, #0x00FF
    movt    r1, #0x00FF          @ r1 = 0x00FF00FF
    and     r2, r0, r1           @ r2 = n & 0x00FF00FF
    lsr     r3, r0, #8           @ r3 = n >> 8
    and     r3, r3, r1           @ r3 = (n >> 8) & 0x00FF00FF
    add     r0, r2, r3           @ n = r2 + r3

    @ 第五步:统计整个32位(掩码0x0000FFFF)
    movw    r1, #0xFFFF          @ r1 = 0x0000FFFF
    and     r2, r0, r1           @ r2 = n & 0x0000FFFF
    lsr     r3, r0, #16          @ r3 = n >> 16
    add     r0, r2, r3           @ n = r2 + r3

    bx      lr                   @ 返回结果

C测试代码

#include <stdio.h>

extern int popcount(int n);

int main(){
    // 测试用例
    printf("%d\n", popcount(5));        // 输出 2
    printf("%d\n", popcount(255));      // 输出 8
    printf("%d\n", popcount(0));        // 输出 0
    printf("%d\n", popcount(-1));       // 输出 32(0xFFFFFFFF)
    printf("%d\n", popcount(0x1234));   // 输出 5(二进制0001 0010 0011 0100)
    return 0;
}

编译运行

1.保存汇编代码:将汇编代码保存为 popcount.s。
2.保存C代码:将C测试代码保存为 main.c。
3.交叉编译(以 arm-linux-gnueabi-gcc 为例):

arm-linux-gnueabi-gcc -mcpu=cortex-a7 -o test main.c popcount.s
qemu-arm ./test

1.3 power of 2

来编写一个函数判断是否是2的次幂;
算法逻辑:num & (num -1)的结果应该为0;

汇编代码

.global _start
_start:
	ldr r0, =4
	bl pow2
	1:	b 1b    // Done


pow2:
    sub     r1, r0, #1      @ r1 = num - 1
    and     r1, r0, r1      @ r1 = num & (num - 1)
    cmp     r1, #0          @ 检查结果是否为0
    moveq   r0, #1          @ 如果是0,返回1(是2的幂)
    movne   r0, #0          @ 否则返回0(不是2的幂)
    bx      lr              @ 返回调用者
	

C语言测试代码

#include <stdio.h>

extern int pow2(unsigned int num);

int main() {
    printf("pow2(1) = %d\n", pow2(1));          // 输出 1
    printf("pow2(2) = %d\n", pow2(2));          // 输出 1
    printf("pow2(4) = %d\n", pow2(4));          // 输出 1
    printf("pow2(3) = %d\n", pow2(3));          // 输出 0
    printf("pow2(8) = %d\n", pow2(8));          // 输出 1
    printf("pow2(7) = %d\n", pow2(7));          // 输出 0
    printf("pow2(0x80000000) = %d\n", pow2(0x80000000)); // 输出 1
    return 0;
}

编译与运行

保存汇编代码:将汇编代码保存为 pow2.s。
保存C代码:将C测试代码保存为 main.c。
交叉编译(以 arm-linux-gnueabi-gcc 为例);

arm-linux-gnueabi-gcc -mcpu=cortex-a7 -o test main.c pow2.s
qemu-arm ./test

1.4 Bit Mask

实现给定参数的N mask;

算法逻辑:

汇编代码

.global _start
_start:
	ldr r0, =4
	bl mask
1:	b 1b    // Done

mask:
    mov     r1, #1          @ 加载常数1到r1
    lsl     r1, r1, r0      @ r1 = 1 << N(左移N位),将 r1 左移 N 位(N 是输入参数,通过 r0 传递)
    subs    r0, r1, #1      @ r0 = (1 << N) - 1
    bx      lr              @ 返回调用者

C语言代码


#include <stdio.h>

extern unsigned int mask(unsigned int N);

int main() {
    // 测试用例
    printf("mask(0)  = 0x%08X\n", mask(0));    // 输出 0x00000000
    printf("mask(1)  = 0x%08X\n", mask(1));    // 输出 0x00000001
    printf("mask(5)  = 0x%08X\n", mask(5));    // 输出 0x0000001F
    printf("mask(31) = 0x%08X\n", mask(31));   // 输出 0x7FFFFFFF
    printf("mask(32) = 0x%08X\n", mask(32));   // 输出 0xFFFFFFFF
    return 0;
}

编译运行

保存汇编代码:将汇编代码保存为 mask.s。
保存C代码:将C测试代码保存为 main.c。
交叉编译(以 arm-linux-gnueabi-gcc 为例):

arm-linux-gnueabi-gcc -mcpu=cortex-a7 -o test main.c mask.s
qemu-arm ./test
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值