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

QQ登录

只需一步,快速开始

搜索
热搜: 下载 VB C 实现 编写
查看: 3305|回复: 2

随口扯几句操作位的事情

[复制链接]
发表于 2018-4-2 19:25:44 | 显示全部楼层 |阅读模式

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

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

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

写成汇编可能会变成这个样子:
  1. rotleft proc x:dword,n:byte
  2.        
  3.         mov eax,x
  4.         mov edx,eax
  5.         mov cl,32
  6.         sub cl,n
  7.         shr eax,cl
  8.         mov cl,n
  9.         shl edx,cl
  10.         or eax,edx
  11.         ret
  12.        
  13. rotleft endp
复制代码

嗯。。我™执行了8条指令才实现,关键性的指令还有四条(shr,shl,sub,or)。。。
但如果我们自己用汇编指令实现的话,直接引入rol,ror指令即可。
写成函数的话就是这个样子:
  1. rotl_ez proc x:dword,n:byte
  2.        
  3.         mov cl,n
  4.         mov eax,x
  5.         rol eax,cl
  6.         ret
  7.        
  8. 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
回复

使用道具 举报

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

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

使用道具 举报

 楼主| 发表于 2018-4-2 23:49:30 | 显示全部楼层
0xAA55 发表于 2018-4-2 11:00
我正想说“旋转位的时候编译器有优化”。但说出这句话之前我突然想到:有的人就是因为太迷信编译器优化而产 ...

见过别人举过编译器优化出了奇怪问题的例子(比如CYY举过VC2010的尾递归优化失误),于是我基本上不敢开编译器优化了,直接就指定/Od了。
回复 赞! 靠!

使用道具 举报

本版积分规则

QQ|Archiver|小黑屋|技术宅的结界 ( 滇ICP备16008837号 )|网站地图

GMT+8, 2024-11-22 11:58 , Processed in 0.033814 second(s), 22 queries , Gzip On.

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

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