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