技术宅的结界

 找回密码
 立即注册→加入我们

QQ登录

只需一步,快速开始

搜索
热搜: 下载 VB C 实现 编写
查看: 278|回复: 2
收起左侧

随口扯几句操作位的事情

[复制链接]

23

主题

63

帖子

1532

积分

用户组: 管理员

UID
1043
精华
7
威望
29 点
宅币
1349 个
贡献
27 次
宅之契约
0 份
在线时间
252 小时
注册时间
2015-8-15
发表于 2018-4-2 19:25:44 | 显示全部楼层 |阅读模式

欢迎访问技术宅的结界,请注册或者登录吧。

您需要 登录 才可以下载或查看,没有帐号?立即注册→加入我们

x
在C里操作位一般都是通过|来置位特定位,&来复位特定位,对应的在汇编里可以用or,and指令实现。
要移动位的话,在C里一般是用<<和>>来左右移位,在汇编里则通过shl和shr指令实现。对于128位数的移位,则通过shld和shrd指令实现。
旋转位的话会很蛋疼,在C里得把边界特定数量位移动到另一侧边界,再将另一部分移动过来。
左旋一个32位变量的话,大概会成这个样子:
[C] 纯文本查看 复制代码
#define rotleft(x,n)	(x<<(32-n))|(x>>n)

写成汇编可能会变成这个样子:
[Asm] 纯文本查看 复制代码
rotleft proc x:dword,n:byte
	
	mov eax,x
	mov edx,eax
	mov cl,32
	sub cl,n
	shr eax,cl
	mov cl,n
	shl edx,cl
	or eax,edx
	ret
	
rotleft endp

嗯。。我™执行了8条指令才实现,关键性的指令还有四条(shr,shl,sub,or)。。。
但如果我们自己用汇编指令实现的话,直接引入rol,ror指令即可。
写成函数的话就是这个样子:
[Asm] 纯文本查看 复制代码
rotl_ez proc x:dword,n:byte
	
	mov cl,n
	mov eax,x
	rol eax,cl
	ret
	
rotl_ez endp

上述代码均为MASM。

关于C里判断特定位是置位还是复位,一般会用and指令结合if实现,实质上是对数做与运算后判断是否等于判断用的常量。在C里这么写无可厚非,但在汇编里,如果你只需要判断一个位,则完全不需要这么搞。
有个汇编指令叫做bt,即bit test的意思,这个指令将操作数的特定位赋值到RFlags的CF位。于是要判断一个特定位就相当简单,第一个操作数放要判断的数,第二个操作数放位偏移即可。
指令执行完成后,由于指定位会直接复制到CF位里,直接通过jc或jnc来跳转就行了。若置位需要执行跳转,结合jc;若复位需要执行跳转,结合jnc。
和bt相关的指令还有btc,btr,bts三条指令。其中c,r,s分别代表complement,reset和set。意思是在bt完成后,分别对特定位进行补位,复位和置位。

追加:
在微软编译器中,有一些钦定的编译器内置宏,其中的一些宏可以用于位操作:

__bittest宏可以生成bt指令并对if语句作优化:https://msdn.microsoft.com/en-us/library/h65k4tze(v=vs.100).aspx
相似的,还有针对btc,btr,bts指令的编译器内置宏,分别是:
__bittestandcomplement:https://msdn.microsoft.com/en-us/library/zbdxdb11(v=vs.100).aspx
__bittestandreset:https://msdn.microsoft.com/en-us/library/hd0hzyf8(v=vs.100).aspx
__bittestandset:https://msdn.microsoft.com/en-us/library/z56sc6y4(v=vs.100).aspx
值得注意的是,如果操作数是64位长的,则应该用对应的64位版本。

__shiftleft128和__shiftright128指令可以实现对128位长的数据进行移位:
__shiftleft128:https://msdn.microsoft.com/en-us/library/szzkhewe(v=vs.100).aspx
__shiftright128:https://msdn.microsoft.com/en-us/library/5az7sk38(v=vs.100).aspx

比较奇葩的是,对于旋转位这玩意,微软是有相关的内置宏的,但是只有针对byte和word长变量的宏,它们分别是:
左旋用的__rotl8和__rotl16:https://msdn.microsoft.com/en-us/library/t5e2f3sc(v=vs.100).aspx
右旋用的__rotr8和__rotr16:https://msdn.microsoft.com/en-us/library/yy0728bz(v=vs.100).aspx
于是对dword左旋,你就愉快的#define __rotl32(x,n)        (x<<(32-n))|(x>>n)吧

需要注意的是,假如你要使用这些内置宏,必须指定编译器将代码视为C预言代码,通过设置/TC编译参数或者令后缀名位.c,绝对不可以有/TP参数!在视代码为C++代码的情况下,编译器则不会钦定这些宏!

参考资料:
《超微半导体AMD64架构程序员手册第三卷》第三章: https://support.amd.com/TechDocs/24594.pdf
flowers for Broken spirits - a woman turned into stake will hold the world in the basin of fire.

995

主题

2207

帖子

5万

积分

用户组: 管理员

一只技术宅

UID
1
精华
197
威望
261 点
宅币
16463 个
贡献
32443 次
宅之契约
0 份
在线时间
1565 小时
注册时间
2014-1-26
发表于 2018-4-2 23:00:23 | 显示全部楼层
我正想说“旋转位的时候编译器有优化”。但说出这句话之前我突然想到:有的人就是因为太迷信编译器优化而产生错误的理解。
不过就在几分钟前,我在给单片机写启动代码的时候,遇到了一个十分蛋疼的问题:我为了不依赖标准库的memset,自己实现了一个,用来清空bss段。
结果在使用gcc编译的时候,因为开了-O3优化,它检测到我使用循环来把一块内存区域清零,于是自动把我的代码换成了一条memset的调用。
但是在链接的时候,因为嵌入式环境缺乏libc库,memset没有实现。于是链接器报错了。
最后我只好用-fno-tree-loop-distribute-patterns的方式来防止编译器做“聪明的事”。

其实我可以实现一个内存到内存DMA来造一个自己的memset的(检测DMA是否正在传输数据,如果空闲,就拿来用)。但考虑到这会让关联的外设运行异常并且难以调试,我就不这么做了。

23

主题

63

帖子

1532

积分

用户组: 管理员

UID
1043
精华
7
威望
29 点
宅币
1349 个
贡献
27 次
宅之契约
0 份
在线时间
252 小时
注册时间
2015-8-15
 楼主| 发表于 2018-4-2 23:49:30 | 显示全部楼层
0xAA55 发表于 2018-4-2 11:00
我正想说“旋转位的时候编译器有优化”。但说出这句话之前我突然想到:有的人就是因为太迷信编译器优化而产 ...

见过别人举过编译器优化出了奇怪问题的例子(比如CYY举过VC2010的尾递归优化失误),于是我基本上不敢开编译器优化了,直接就指定/Od了。
flowers for Broken spirits - a woman turned into stake will hold the world in the basin of fire.

本版积分规则

QQ|申请友链|Archiver|手机版|小黑屋|技术宅的结界 ( 滇ICP备16008837号|网站地图

GMT+8, 2018-9-24 00:44 , Processed in 0.092963 second(s), 32 queries , Gzip On.

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表