通常计算机上的加法运算是循环的,例如 0x0001 + 0x7fff = 0x8000, 其中0x7fff是short类型的最大正值,0x8000是最小负值。假设我们是把两个声音相加,由于结果的符号反转,将出现不连续,如果我们把上面的数当做Q15定点小数来看的话,本来是正1(0x7fff),由于加了个很小的数(0x0001),结果却变为了负1(0x8000)。
因此DSP芯片都支持饱和加法,当结果超出范围的时候,就取范围的上下限为结果:0x0001 + 0x7fff = 0x7fff。例如在5510芯片中,为了使用饱和加法只需要把状态位:SATA或SATD设为1即可(SATA和SATD的具体含义请参考手册)。这种位操作只能在汇编下进行,因此为了方便C语言编程,编译器提供了伪函数_sadd和_ssub。使用这些伪函数的时候,编译器会自动添加设置和清除饱和位的语句,例如:
a=_sadd(a,b)可能会编译成
BSET ST3_SATA ;设置饱和位
ADD T0, T1
BCLR ST3_SATA ;清除饱和位
当几个_sadd连续使用的时候,编译器只设置和清除一次饱和位。但是当几个_sadd调用中间还有一般的加法的话,就会设置和清除好几次饱和位了。
例如
c= _sadd(c,b); // 设置并清除一次
c= a+c;
c= _sadd(c,b); // 再设置并清除一次
如果在一个循环中有多次这样的交叉调用的话,饱和位将被设置和清除很多次,这样显然是浪费DSP资源的。因此尽量把饱和加减的调用集中,为了减少设置清除饱和位的次数,一些不需要饱和运算的加减也可以使用饱和运算。如果可以把整个函数都改写为饱和加减运算的话,那么干脆在函数最前面添加asm(" BSET ST3_SATA");手工设置饱和位,这样程序中可以直接是用加减号运算。