每当您必须将一个位块从源传输到目标时,过程总是相同的,并且可以通过三个参数来描述:
SRC_MASK
与源操作数进行 AND 运算的值。
SRC_SHIFT
将源操作数移动多少。实际上,这也应该指定一个方向,无论是左还是右。我们将在这里假设左移。
在原生支持旋转操作的 CPU 中,我们可以使用它们来摆脱对方向的需求。
DST_MASK
与目标操作数进行 AND 运算的值。
那么算法就是
dst = (dst & DST_MASK) | (src & SRC_MASK) << SRC_SHIFT;
这是一个通用算法,具体的实例可以导致具体的优化,作为一个简单的例子考虑所有位的移动,这是一个简单的“移动”。
由于通常掩码具有寄存器的大小,因此在 MIPS 中,这需要经典使用 lui/ori 对。
或者,我们可以像您一样使用移位来屏蔽源操作数的不相关位,但这不适用于清除目标中的非连续位块(实际上您没有)。
在 MIPS 中,上面的 C 代码是:
lui $t0, SRC_MASK >> 16
ori $t0, SRC_MASK & 0xffff ;t0 = SRC_MASK
lui $t1, DST_MASK >> 16
ori $t1, DST_MASK & 0xffff ;t1 = DST_MASK
and $t2, $1, $t0 ;t2 = src & SRC_MASK
sll $t2, SRC_SHIFT ;t2 = (src & SRC_MASK) << SRC_SHIFT
and $2, $2, $t1 ;$2 = dst & DST_MASK
or $2, $2, $t2 ;$2 = (dst & DST_MASK) | (src & SRC_MASK) << SRC_SHIFT
对于有问题的问题,掩码的值和移位量是:
SRC_MASK = 0x000007e0h
SRC_SHIFT = 5
SRC_MASK = 0xffff03ffh
关于字节序的说法
我不喜欢“字节序”这个词来排序位,它与byte ordering 的关系太大了。
给定一个词,我们可以用两种方式标记它的位:
31 23 17 7 0 0 7 15 24 31
+------+------+------+------+ +------+------+------+------+
| | | |
+------+------+------+------+ +------+------+------+------+
Little endian Big endian
这是一个对人类有用的约定,从 CPU 的角度来看,没有“位字节序”之类的东西,因为寄存器只是保存一组(无论哪种方式)有序位,而不是数字:它是提供这些设定了一个意义。
所以问题是提到“小端”只是为了澄清作者在谈到位时想到了左标签。
简单地说,“给定数字 12345,第 2 位是哪一个?”这个问题并没有被普遍回答为“4”,一些数学背景很少的人倾向于回答“2”。他们还倾向于将“第二个数字”(即 4)与“index/position 2”(即“3”)混淆"(因为索引从 0 开始,没有第 0 位)。
为了避免所有这些误解,作者澄清了它们的意思,尽管在我看来,使用了一个糟糕的术语。
我个人将左侧的标签称为“自然排序”(因为设置位 X 我们与 2X 或)和右侧的标签作为“逆序”。