5.6 含有一个常数的复数乘法
复数的乘法由四个乘法和两个加法构成。PMADDWD正是这样的指令。为了使用该指令,仅需将数据格式化为四个16位值。每个实部和虚部值应为16位值。
令输入数据为Dr和Di
这里 Dr=数据的实部
Di=数据的虚部
将复数的常系数格式化为内存中的四个16位值[Cr -Ci Ci Cr]。
记住使用MOVQ指令将值取出送到MMX™寄存器中。
输入:
MM0: 由Dr,Di构成的复数
MM1: 在格式[Cr -Ci Ci Cr]中的常复数系数
输出:
MM0: 包含[Pr Pi]的两个32位双字复数,
其实部是由Pr=Dr*Cr-Di*Ci生成,
复数的虚部是由Pi=Dr*Ci+Di*Cr生成。
PUNPCKLDQ MM0,MM0 ;生成[Dr Di Dr Di]
PMADDWD MM0,MM1 ;结果为[(Dr*Cr-Di*Ci)(Dr*Ci+Di*Cr)]
注意,输出为一个字组。如果需要,可用一条成组指令将结果转换为16位值(与输入格式匹配)。
5.7 无符号数差值的绝对值
本例计算两个无符号数差值的绝对值。假设是一个无符号字节组数据类型。这里,我们使用无符号饱和模式下的减法指令。指令按无符号饱和方式将无符号操作数相减。仅支持字节组和字组,不支持双字组。
输入:
MM0: 源操作数
MM1: 源操作数
输出:
MM0: 无符号数差值的绝对值
MOVQ MM2, MM0 ;制做MMO的备份
PSUBSB MM0, MM1 ;计算差值方法1
PSUBSB MM1, MM2 ;计算差值方法2
POR MM0, MM1 ;将两个差值求或
如果操作数有符号,本例不执行。有关有符号差值的绝对值请见下一个例子。
5.8 有符号数差值的绝对值
本例计算两个有符号数差值的绝对值。MMX™指令中没有按无符号饱和方式对有符号操作数进行减法的操作。这里使用的技巧是,先将两个输入的操作数的对应元素按大小排序,生成一个较大的字组数据和一个较小的字组数据。然后,用较大的值减去较小的值,就生成了所要求的差值的绝对值。关键是由B=XOR(A,XOR(A,B))和A=XOR(A,0)构成的快速排序技术。然后在成组数据类型中,使某些元素为XOR(A,B),某些为0,你可以将这一操作数与A进行XOR操作,并在某些地方生成A,某些地方生成B。下例假设一个字组数据类型,每个元素为一个有符号数值。
输入:
MM0: 有符号源操作数
MM1: 有符号源操作数
输出:
MM0: 有符号数的差值的绝对值
MOVQ MM2, MM0 ;制做源操作数1的备份(A)
PCMPGTW MM0, MM1 ;取得源操作数1>源操作数2的标志
MOVQ MM4, MM2 ;制做源操作数A的另一个备份
PXOR MM2, MM1 ;生成交换操作XOR(A,B)的中间值
PAND MM2, MM0 ;生成0s和XOR(A,B)元素的标志
;当A>B为值XOR(A,B),当A<=B为值0
MOVQ MM3, MM2 ;生成交换掩码的备份
PXOR MM4, MM2 ;XOR(A,swap mask)为较小值
PXOR MM1, MM3 ;XOR(B,swap mask)为较大值
PSUBW MM1, MM4 ;差值的绝对值=较大值-较小值
5.9 绝对值
当X为有符号数时,计算|X|。下例假设操作数为有符号字。
输入:
MM0: 有符号源操作数
输出:
MM1: ABS(MM0)
MOVQ MM1, MM0 ;备份X
PSRAW MM0, 15 ;复制符号位(是双字则用31位)
PXOR MM0, MM1 ;如负数则取1的补码
PSUBS MM1, MM0 ;如为负数则加1
注意 最大的负数(即16位的8000H)所求的绝对值不正确,但该代码对这种情况也是合理的,它使减一的结果为7fffH。
5.10 有符号数截取到任意有符号区域[HIGH,LOW]
本例说明了如何将一个有符号值截取到有符号区域[HIGH,LOW]。特别是当值小于LOW或大于HIGH时,截取到相应的LOW或HIGH中。本技术按无符号饱和方式进行成组数据加和成组数据减指令,该技术只能用于字节组和字组数据类型。
下例使用了最大成组数和最小成组数。
下例显示了对字数据值的操作。为简便起见,使用下列常数(在对字节值操作时,使用相应的常数)。
PACKED_MAX=0x7FFF7FFF7FFF7FFF
PACKED_MIN=0x8000800080008000
PACKED_LOW包含了由LOW值构成的、字组数据类型中的全部4个字。
PACKED_HIGH包含了由HIGH值构成的、字组数据类型中的全部4个字。
PACKED_USMAX为全1。
HIGH_US将HIGH值加到PACKED_MIN中的全部元素上。
LOW_US将LOW值加到PACKED_MIN中的全部元素上。
本例显示了对字数据值的操作。
输入:
MM0: 有符号源操作数
输出:
MM0: 按无符号区域[HIGH,LOW]截取的有符号操作数
PADD MM0, PACKED_MIN ;非饱和方式相加并转换为无符号数
PADDUSW MM0, (PACKED_USMAX-HIGH_US)
;截取HIGH
PSUBUSW MM0, (PACKED_USMAX-HIGH_US+L0W_US)
;截取到LOW
PADDW MM0, PACKED_LOW ;取消前两个偏移量
上述代码先将值转换为无符号数,然后将它们截取到一个无符号区域。最后一条指令将数据转换为有符号数据,并置于有符号区域中。当数据(HIGH-LOW)<0x8000时,将数据转换为无符号数的结果是正确的。
如果(HIGH-LOW)>=0x8000,算法应按下面所示进行简单修改。
MM0: 有符号源操作数
输出:
MM0: 按无符号区域[HIGH,LOW]截取的有符号操作数
PADDSSW MM0, (PACKED_USMAX-HIGH_US) ;截取HIGH
PSUBSSW MM0, (PACKED_USMAX-PACKED_HIGH+PACKED_LOW)
;截取到LOW
PADDW MM0, LOW ;取消前两个偏移量
如果已知(HIGH-LOW)>=0x8000,本算法则省下一个时钟周期。为了了解为什么在(HIGH-LOW)<0x8000时,这个由三条指令构成的算法不能工作,注意0xFFFF减任何小于0x8000的数都将产生一个在数量级上大于负数0x8000的数。当PSUBSSW MM0, (0xFFFF-HIGH+LOW)(三行算法的第二条指令)被执行时,一个负数将被减,造成MM0中的值不减反加,这样就产生了一个不正确的结果。
5.11 无符号数截取到任意无符号区域[HIGH,LOW]
本例说明了如何将一个无符号数截取到无符号区域[HIGH,LOW]。如果数据小于LOW或者大于HIGH,那么截取到对应的LOW或HIGH中。本技术按无符号饱和方式进行成组数据加和成组数据减指令,该技术只能用于字节组和字组数据类型。
下例显示了对字数据值的操作。
输入:
MM0: 无符号源操作数
输出:
MM0: 按无符号区域[HIGH,LOW]截取的无符号操作数
PADDUSW MM0, 0xFFFF-HIGH ;截取到HIGH
PSUBUSW MM0, (0xFFFF-HIGH+LOW) ;截取到LOW
PADDW MM0, LOW ;取消前两个偏移量
5.12 常数生成
在MMX™指令集中不存在能够将立即数常数取到MMX™寄存器的指令。下列代码段用于生成MMX™寄存器中的常用常数。当然,也可以将常数作为内存中的局域变量。但是如果要这样做,就要在内存中进行数据的复制,以便可用MOVQ指令读取。
在MM0中产生0:
PXOR MM0, MM0
在MM1中产生全1数,每个成组数据类型字段为-1
PCMPEQ MM1, MM1
为每个字节组[或成组字](或双字组)字段产生常数1
PXOR MM0, MM0
PCMPEQ MM1, MM1
PSUBB MM0, MM1 [PSUBW MM0, MM1] (PSUBD MM0, MM1)
在每个字组(或双字组)中生成有符号常数2n-1
PCMPEO MM1, MM1
PSRLW MM1, 16-n (PSRLD MM1, 32-n)
在每个字组(或双字组)字段中生成有符号常数-2n
PCMPEO MM1, MM1
PSLLW MM1, n (PSLLD MM1, n)
由于MMX™指令集不支持字节的移位指令,2n-1和-2n只与字组和双字组相关。