- UID
- 1043
- 精华
- 积分
- 11692
- 威望
- 点
- 宅币
- 个
- 贡献
- 次
- 宅之契约
- 份
- 最后登录
- 1970-1-1
- 在线时间
- 小时
|
在C里操作位一般都是通过|来置位特定位,&来复位特定位,对应的在汇编里可以用or,and指令实现。
要移动位的话,在C里一般是用<<和>>来左右移位,在汇编里则通过shl和shr指令实现。对于128位数的移位,则通过shld和shrd指令实现。
旋转位的话会很蛋疼,在C里得把边界特定数量位移动到另一侧边界,再将另一部分移动过来。
左旋一个32位变量的话,大概会成这个样子:
- #define rotleft(x,n) (x<<(32-n))|(x>>n)
复制代码
写成汇编可能会变成这个样子:
- 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指令即可。
写成函数的话就是这个样子:
- 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 |
|