******************************************************************************
******************************************************************************
****                 TMS320C54x用ADPCM编解码程序V0.1                      ****
****                      All right reserved                              ****
****                         版权所有                                     ****
****                     程序设计:罗茂才                                  **** 
****                 E-mail: luomc@neusoft.com                            ****
****                         luomao2000@163.net                           ****
****                         luomao1977@sina.com                          ****
****                                                                      ****
****  本程序参照Stichting Mathematisch Centrum, Amsterdam,The Netherlands ****
****  所写的C语言ADPCM编解码程序设计，作者不承担任何可能侵犯版权的后果。  ****
****                                                                      ****
****  允许自由使用、复制、修改、传播该程序，可免费用于商业用途，但不得以  ****
****  该程序作为主体获得利益。无论在什么情况下使用，都请保留该版权信息。  ****                 
****  作者不保证该程序能完全正确按照你的要求运行，所有由于使用该程序      ****
****  造成的损失，后果自负（呵呵，我可没有特意留下什么隐患啊）。          ****
****  如果你利用了该程序，请告诉一声(我想知道有多少人会使用这个程序)。    ****
****  如果你对该程序有任何建议、错误信息反馈等，敬请与作者联系            ****
****  (请采用Algebraic assembly方式汇编该文件)                            ****
****                                                                      ****
****            感谢我的老婆给予我的爱、关心以及支持。                    **** 
****                                                  2003年7月31日       ****
******************************************************************************
******************************************************************************

*
* 编码函数C语言声明
* extern void AdpcmEncoder(int* pBufferIn, int* pBufferOut, 
*		   			       int* pPrevValue,int* pPrevIndex, int Length);
* 解码函数C语言声明（解码长度为实际解码后长度的1/4）
* extern void AdpcmDecoder(int* pBufferIn, int* pBufferOut, 
*					       int* pPrevValue,int* pPrevIndex, int Length);
*
* 编解码后的PrevValue和PrevIndex都被修改，在后续的编解码中将继续被使用
*
* CCS2.1下测试通过

 .title "Intel/DVI ADPCM coder/decoder"
 .def _AdpcmEncoder
 .def _AdpcmDecoder
 .mmregs

 ;索引表 
 .data
IndexTable:    ; 实际上只用了15个，考虑到速度问题，有一个舍弃了 
   .word  -8, -5, -3, -1, 1, 3, 5, 8
   .word  8,  5,  3,  1, -1,-3,-5,-8
   
 ;步进量   
StepSizeTable:  ; 共84个
   .word     4,     5,     6,     7,    8,     9,   10,    11,    12,    14
   .word    16,    18,    20,    22,   25,    28,   32,    36,    42,    49
   .word    57,    67,    78,    90,  103,   118,  134,   152,   170,   189 
   .word   209,   230,   253,   279,  307,   337,  371,   408,   449,   494
   .word   544,   598,   658,   724,  796,   876,  963,  1060,  1166,  1282
   .word  1411,  1552,  1707,  1878, 2066,  2272, 2499,  2749,  3024,  3327
   .word  3660,  4026,  4428,  4871, 5358,  5894, 6484,  7132,  7845,  8630
   .word  9493, 10442, 11487, 12635,13899, 15289,16818, 18500, 20350, 22385
   .word 24623, 27086, 29794, 32767
 
    
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
; 寄存器使用规则(调用时不设置)
; AR0: 临时变量
; AR2: 输入缓冲区指针
; AR3: 输出缓冲区指针
; AR4: 步进量地址指针
; AR5: 当前输出数据计数器(0--3: 16位长度可保存4个编码数据)
	.asg *SP(3), pBufferOut
	.asg *SP(4), pPrevValue
	.asg *SP(5), pPrevIndex
	.asg *SP(6), Length
	
	.asg *SP(0), PrevValue      ; 占用了这两个堆栈位置，存放临时数据
	.asg *SP(1), PrevIndex      ;
   .text
_AdpcmEncoder:
    TC = (*SP(4) == #0)         ; 首先检测缓冲区长度是否为0，为0则结束
    if(TC) dgoto EncoderEnd
    AR5 = #4                    ; 设置计数器初值
    SP += -2                    ; 调整堆栈指针，以便利用其中的两个子
	AR2 = A                     ; 读取输入缓冲区地址
    B = pBufferOut              ; 读入输出缓冲区地址
    AR3 = B
    B = pPrevValue              ; 读入上次转换值地址 
    AR0 = B
    ASM = #0
    SXM = 1                     ; 设置符号位扩展
    Length -= 1                 ; 长度减1
    A = pPrevIndex              ; 读取上次转换的索引
    B = *AR0                  
    AR0 = A
    PrevValue = B               ; 读取上次转换值        
    FRCT = 0 
    A = *AR0                    
    PrevIndex = A               ; 保存上次转换索引
    A = Length                  ; 读取转换长度 
    BRC = A                     ; 设置块循环计数器
    A = PrevIndex 
    DBLOCKREPEAT(EncoderLoopEnd-1)    ; 启动块循环（根据循环最后一条指令确定是1还是2）
    *AR3 = #0                   ; 输出缓冲区第一个字清零
EncoderLoop:
    A += #StepSizeTable         ; 偏移 
    AR4 = A                     ; 取得步进基地址 + 偏移
    B = *AR2+                   ; 取得输入缓冲区的值(没有必要规范到-32768...+32767)
    B -= PrevValue              ; 获得当前值与上次计算值之间的差值
	
    A = |B|                     ; 计算delta=diff/step，并将商规范到-8...+7
    REPEAT(#16)                 ; 循环减（做除法）多除了一次，相当于商×2
    SUBC(*AR4, A) 
    A += #1                     ; 调整0.5的差值
    A &= #0FFFFh                ; (清除高位无效数据) 
    A = A >> 1
    if(BGEQ) DGOTO EncoderSetDelta  ; 检测是否是负数
    B = #7                      ; 将Delta数据规范到-8...+7
    A = MIN(A, B)
    
    A = -A                      ; 是负数，商取负
EncoderSetDelta:    
    ;B = #-8                    ; 已经规范到0--7了，取负数后也没有关系
    ;A =MAX(A, B)

    T = A    
                                ; 计算PrevValue值,并将其规范到(-32768)--(+32767) 
    B = T*(*AR4)                ; 获得Delta*Step的值，保存在B寄存器中
    B += PrevValue              ; 计算上次值与这次差值的和
    ;B += *AR4 >> 1
    B = B <<C 8                 ; 只有32位的饱和运算
    B = B <<C 8                 ; 移位操作比用max/min指令要快 
    saturate(B)                 ; 饱和成32位的数据
    
    ; 检测是否需要将输出指针移动到下一个字
    if(*AR5- != 0) DGOTO EncoderNoInc
    A &= #0Fh                    ; 保留低4位

    AR5 = #3                     ; 重新设置计数器
    *+AR3 = #0                   ; 输出缓冲区清零
EncoderNoInc:
    PrevValue = hi(B)            ; 保存当前转换结果
    B = A + *AR3 << 4            ; 将输出缓冲区当前字左移4位，然后加上当前值 
    *AR3 = B                     ; 
    
    AR0 = A						 ; 取得更新后的索引 
    B = #0
    A = PrevIndex
    A += *AR0(IndexTable)        ; 基地址 + 偏移
    
    A = MAX(A, B)                ; 将PrevIndex规范到0-83
    B = #83
    A = MIN(A, B)
    PrevIndex = A
EncoderLoopEnd:    
    B = pPrevValue               ; 设置转换后的PrevIndex和PrevValue
    AR0 = B
    A = pPrevIndex
    AR2 = A
    A = PrevIndex
    B = PrevValue 
    *AR2 = A
    SP += 2                      ; 将堆栈指针调整回原来的值
    *AR0 = B
    NOP                          ; 避免冲突用的
EncoderEnd:    
	RETURN
	
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;    
; 寄存器使用规则(调用时不设置)
; AR0: 临时变量
; AR1: 当前转换值
; AR2: 输入缓冲区指针
; AR3: 输出缓冲区指针
; AR4: 步进量地址指针
; AR5: 当前输出数据计数器(0--3: 16位长度可保存4个编码数据)
	.asg *SP(3), StepValue        ; 临时变量，存放当前的输入处理值（不断左移4位）
	
_AdpcmDecoder:
	TC = (*SP(4) == #0) 
	if(TC) DGOTO DecoderEnd       ; 如果长度为0，退出
	AR5 = #3                      ; 设置循环计数器 
	SP -= 2                       ; 调整堆栈指针，以便有多余的两个字可供临时使用
	AR2 = A                       ; 取输入缓冲区地址 
	A = pBufferOut                ; 取输出缓冲区地址
    B = pPrevValue                ; 取存放上次转换值变量的地址
    AR0 = B
    ASM = #0                      ; (好像没有用)
    SXM = 1                       ; 设置符号位扩展
	AR3 = A                       ; 取得输出缓冲区
    B= *AR0                       ; 取得PrevValue 
    A = pPrevIndex                ; 取PrevIndex的地址 
    AR0 = A                       ; 临时存放地址(直接存会出错(编译得就不对)) 
    PrevValue = B                 ; 呵呵，终于取出来了  
    OVM = 1                       ; 溢出吧，我的兄弟，就等你了
    A = *AR0                      ; 读取PrevIndex的值
    PrevIndex = A
	A = Length<<2                 ; 因为是16位操作，可存放4个转换值，乘以4 
	A -= #1                       ; 减去1，循环次数要比实际次数少1
	BRC = A                       ; 设置块循环计数器 
	A = *AR2+                     ; 取输入缓冲区的值
	
	DBLOCKREPEAT(DecoderLoopEnd-1) ; 运转起来吧，要不然饭都凉了
	StepValue = A                 ; 获得当前处理值
	A = PrevIndex				  ; 取得当前index所对应的值
DecoderLoop:
	A += #StepSizeTable
	AR4 = A
	A = StepValue << 4            ; 取当前转换值,获得delta值(利用了符号扩展功能)
	StepValue = A      
	B = PrevValue
	B = B + hi(A)*(*AR4)          ; 获得计算的差值
    ;B += *AR4 >> 1
    B = B <<C 8                   ; 因为只能进行32位的饱和运算，没有办法啦      
    B = B <<C 8                   ; 将数据规范到-32768...+32767
    saturate(B)
    
	if(*AR5- != 0) DGOTO DecoderNoInc
    *AR3+ = hi(B)
    PrevValue = hi(B)
	
	AR5 = #3                      ; 重新设置计数器
	B = *AR2+
	StepValue = B
DecoderNoInc:
    
	A = A >> 16                   ; 调整PrevIndex的值
	A &= #0Fh                     ; 并将其调整在0...83之间
    AR0 = A
    B = #0
    A = PrevIndex
    A += *AR0(IndexTable)
    A = MAX(A, B)                 
    B = #83
    A = MIN(A, B)
    PrevIndex = A
DecoderLoopEnd:    
    B = pPrevValue                 ; 设置PrevValue和PrevIndex
    AR0 = B
    B = PrevValue
    A = pPrevIndex
    AR2 = A
    A = PrevIndex
    *AR0 = B
    *AR2 = A
    SP += 2                        ; 将堆栈指针调整回原来的值
    NOP                            ; 避免冲突用的
DecoderEnd:    
    RETURN
    
 .end
 