/*
 *	framadp.c
 *
 *	2-bit ADPCM Decoder
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2001-2014 Naoyuki Sawa
 *
 *	* Wed Dec 20 00:13:10 JST 2006 Naoyuki Sawa
 *	- 作成開始。
 *	* Wed Dec 20 17:31:02 JST 2006 Naoyuki Sawa
 *	- 1st リリース。
 *	- ASM版のadpcm_mix1()関数は、まだまだ最適化不足です。
 *	  インターロックの回避と遅延スロットを活用して、もっと高速化できます。
 *	  今後、実際のアプリケーションで性能を試しながら、高速化する予定です。
 *	* Thu Dec 21 11:18:32 JST 2006 Naoyuki Sawa
 *	- ADPCMCHANNEL構造体を扱い易く並び替えて、ASM版adpcm_mix1()を最適化しました。
 *	* Fri Dec 22 00:19:41 JST 2006 Naoyuki Sawa
 *	- P/ECE上のデコーダ実装を簡単にするため、係数テーブルを変更しました。
 *	  エンコーダ(dppcmcnv.exe)にも、同じ変更を行いました。
 *	  詳しくは、clip/keep/2-bit ADPCMの参考資料.7z/Adpcm2bit/main.cを参照してください。
 *	* Mon Jul 15 20:32:11 JST 2013 Naoyuki Sawa
 *	- ADPCMCHANNEL構造体を並び替え、volフィールドを追加しました。
 *	- 音量設定機能に対応するため、adpcm_att()関数を追加しました。
 *	- 高速RAMを節約するため、係数テーブルをframadp.cからclipadp.cへ移動しました。
 *	* Mon Jul 07 01:54:09 JST 2014 Naoyuki Sawa
 *	- 音量計算の「>>8」の処理をmix1でチャネル毎に行っていたのを、mix2で全チャネル加算後に1回で行うように最適化しました。
 */
#include "clip.h"

/****************************************************************************
 *	内部関数
 ****************************************************************************/

//{{2013/07/15削除:高速RAMを節約するため、係数テーブルをframadp.cからclipadp.cへ移動しました。
////static const short adpcm_tbl[4/*ADPCM値*/][2] = {
////	{  128, 233 },	/* + 0,4 */
////	{  384, 375 },	/* + 4,4 */
////	{ -128, 233 },	/* + 8,4 */
////	{ -384, 375 },	/* +12,4 */
////};			/* =16   */
////↓* Fri Dec 22 00:19:41 JST 2006 Naoyuki Sawa
//#define A	233	/* =(    1.1^-1)*256 */
//#define B	375	/* =(    1.1^ 4)*256 */
//#define C	141	/* =(0.5/1.1^-1)*256 */
//#define D	263	/* =(1.5/1.1^ 4)*256 */
//static const short adpcm_tbl[4/*ADPCM値*/][2] = {
//#ifndef ADPCM_ASM
//	{ A,  C },
//	{ B,  D },
//	{ A, -C },
//	{ B, -D },
//#else /*ADPCM_ASM*/
//	/* ASM版では、ワード単位でいちどに取得して、下位ハーフ、上位ハーフの順に利用します。
//	 * 上位ハーフを下位ハーフへ移動する際、"xsra %rd,16"では2命令に展開されますが、
//	 * 以下のように、予め上位ハーフをSwapしておけば、"swap %rd,%rs"1命令で移動できます。
//	 */
//	{ A, SWAPH( C) },
//	{ B, SWAPH( D) },
//	{ A, SWAPH(-C) },
//	{ B, SWAPH(-D) },
//#endif /*ADPCM_ASM*/
//};
//#undef A
//#undef B
//#undef C
//#undef D
//}}2013/07/15削除:高速RAMを節約するため、係数テーブルをframadp.cからclipadp.cへ移動しました。

/*--------------------------------------------------------------------------*/

#ifndef ADPCM_ASM
void
adpcm_mix1(ADPCMCHANNEL* pch, int mixbuf[/*ADPCMBUFLEN*/])
{
	int n;
	int adpcm;
	int loop;
	//
//{{2013/07/15変更
//	int pcm                  = pch->pcm;
//	int data                 = pch->data;
//	int delta                = pch->delta;
//	int progress             = pch->progress;
//	int rate                 = pch->rate;
//	int len                  = pch->len;
//	const unsigned char* ptr = pch->ptr;
//↓2013/07/15変更
	const unsigned char* ptr = pch->ptr;
	int len                  = pch->len;
	int pcm                  = pch->pcm;
	int data                 = pch->data;
	int delta                = pch->delta;
	int progress             = pch->progress;
	const int rate           = pch->rate;
	const int vol            = pch->vol;
//}}2013/07/15変更

	n = ADPCMBUFLEN;
	do {
		progress -= rate;
		if(progress < 0) {
			progress += SPEAKER_FREQUENCY;
			if(data < (1<<2)) {		/* ASM版では、adpcm_tblへのオフセットとして×4しておきます。 */
				data = *ptr++ | (1<<8);	/* C版とは、dataが保持する値が異なることに注意してください。 */
			}
			adpcm = data & 3;
			data >>= 2;
			//pcm  += (delta * adpcm_tbl[adpcm][0]) >> 8;
			//delta = (delta * adpcm_tbl[adpcm][1]) >> 8;
			//↓* Fri Dec 22 00:19:41 JST 2006 Naoyuki Sawa
			delta = (delta * adpcm_tbl[adpcm][0]) >> 8;
			pcm  += (delta * adpcm_tbl[adpcm][1]) >> 8;
		}
//{{2013/07/15変更
//		*mixbuf++ += pcm;
//↓2013/07/15変更
//		*mixbuf++ += (pcm * vol) >> 8;	/* pcm値は(pcm>32767)及び(pcm<-32768)となり得るので、ASM版では(pcm*vol)に32bit乗算(mlt.w)を用いること。 */
//↓2014/07/07変更:音量計算の「>>8」の処理をmix1でチャネル毎に行っていたのを、mix2で全チャネル加算後に1回で行うように最適化しました。
		*mixbuf++ += (pcm * vol);	/* pcm値は(pcm>32767)及び(pcm<-32768)となり得るので、ASM版では(pcm*vol)に32bit乗算(mlt.w)を用いること。 */
//}}2014/07/07変更:音量計算の「>>8」の処理をmix1でチャネル毎に行っていたのを、mix2で全チャネル加算後に1回で行うように最適化しました。
		if(!--len) {
			loop = pch->loop - 1;
			if(loop >= 0) {
				pch->loop = loop;
				if(!loop) break;
			}
//{{2013/07/15変更
//			pcm      = 0;
//			data     = 0;
//			delta    = 16;
//			progress = 0;
//			len      = pch->init_len;
//			ptr      = pch->init_ptr;
//↓2013/07/15変更
			ptr      = pch->init_ptr;
			len      = pch->init_len;
			pcm      = 0;
			data     = 0;
			delta    = 16;
			progress = 0;
//}}2013/07/15変更
		}
	} while(--n);

//{{2013/07/15変更
//	pch->pcm      = pcm;
//	pch->data     = data;
//	pch->delta    = delta;
//	pch->progress = progress;
//	pch->rate     = rate;
//	pch->len      = len;
//	pch->ptr      = ptr;
//↓2013/07/15変更
	pch->ptr      = ptr;
	pch->len      = len;
	pch->pcm      = pcm;
	pch->data     = data;
	pch->delta    = delta;
	pch->progress = progress;
//	pch->rate     = rate; ⇒ 書き戻し不要
//	pch->vol      = vol;  ⇒ 書き戻し不要
//}}2013/07/15変更
}
#else /*ADPCM_ASM*/
//asm("
//	.code
//	.align 1
//	.global adpcm_mix1
//adpcm_mix1:
//	; %r12 = pch
//	; %r13 = mixbuf
//	pushn	%r3
//	xld.w	%r0, [%r12+ 4]		; %r0  = pcm      = pch->pcm
//	xld.h	%r1, [%r12+ 8]		; %r1  = data     = pch->data
//	xld.h	%r2, [%r12+10]		; %r2  = delta    = pch->delta
//	xld.h	%r3, [%r12+12]		; %r3  = progress = pch->progress
//	xld.h	%r4, [%r12+14]		; %r4  = rate     = pch->rate
//	xld.w	%r5, [%r12+16]		; %r5  = len      = pch->len
//	xld.w	%r6, [%r12+20]		; %r6  = ptr      = pch->ptr
//	xld.w	%r7,  320		; %r7  = ADPCMBUFLEN
//	xld.w	%r10, 16000		; %r10 = SPEAKER_FREQUENCY
//	xld.w	%r11, 256		; %r11 = (1<<8)
//	xld.w	%r14, adpcm_tbl		; %r14 = adpcm_tbl
//adpcm_mix1_L10:				; do {
//	sub	%r3, %r4		;   progress -= rate 
//	xjrge	adpcm_mix1_L30		;   if(progress < 0) {
//	add	%r3, %r10		;     progress += SPEAKER_FREQUENCY
//	xcmp	%r1, 16			;     if(data < (1<<(2+2))) {
//	xjrge	adpcm_mix1_L20
//	ld.ub	%r1, [%r6]+		;       data = (*ptr++ | (1<<8)) << 2
//	or	%r1, %r11
//	xsla	%r1, 2
//adpcm_mix1_L20:				;     }
//	xand	%r15, %r1, 12		;     %r15 = adpcm = data & (3<<2)
//	xsra	%r1, 2			;     data >>= 2
//	add	%r15, %r14		;     %r15 = adpcm_tbl[adpcm]
//	ld.h	%r9, [%r15]+		;     pcm  += (delta*adpcm_tbl[adpcm][0]) >> 8
//	mlt.h	%r9, %r2
//	ld.w	%r9, %alr
//	xsra	%r9, 8
//	add	%r0, %r9
//	ld.h	%r9, [%r15]		;     delta = (delta*adpcm_tbl[adpcm][1]) >> 8
//	mlt.h	%r9, %r2
//	ld.w	%r2, %alr
//	xsra	%r2, 8
//adpcm_mix1_L30:				;   }
//	ld.w	%r15, [%r13]		;   *mixbuf++ = pcm
//	add	%r15, %r0
//	ld.w	[%r13]+, %r15
//	xsub	%r5, %r5, 1		;   if(!--len) {
//	xjrne	adpcm_mix1_L50
//	xld.w	%r15, [%r12+0]		;     %r15 = loop = pch->loop - 1
//	xsub	%r15, %r15, 1
//	xjrlt	adpcm_mix1_L40		;     if(loop >= 0) {
//	xld.w	[%r12+0], %r15		;       pch->loop = loop
//	xjreq	adpcm_mix1_L60		;       if(!loop) break
//adpcm_mix1_L40:				;     }
//	xld.w	%r0, 0			;     pcm      = 0
//	xld.w	%r1, 0			;     data     = 0
//	xld.w	%r2, 16			;     delta    = 16
//	xld.w	%r3, 0			;     progress = 0
//	xld.w	%r5, [%r12+24]		;     len      = pch->init_len
//	xld.w	%r6, [%r12+28]		;     ptr      = pch->init_ptr
//adpcm_mix1_L50:				;   }
//	xsub	%r7, %r7, 1		; } while(--n)
//	xjrne	adpcm_mix1_L10
//adpcm_mix1_L60:
//	xld.w	[%r12+ 4], %r0		; pch->pcm      = pcm
//	xld.h	[%r12+ 8], %r1		; pch->data     = data
//	xld.h	[%r12+10], %r2		; pch->delta    = delta
//	xld.h	[%r12+12], %r3		; pch->progress = progress
//	xld.h	[%r12+14], %r4		; pch->rate     = rate
//	xld.w	[%r12+16], %r5		; pch->len      = len
//	xld.w	[%r12+20], %r6		; pch->ptr      = ptr
//	popn	%r3
//	ret
//");
//↓* Thu Dec 21 11:18:32 JST 2006 Naoyuki Sawa
//↓- ADPCMCHANNEL構造体を扱い易く並び替えて、ASM版adpcm_mix1()を最適化しました。
//↓- 容量の少ない高速RAMに置くために、高速化一辺倒でなく、省メモリにも重点を置いています。
//↓  高速化一辺倒の実装も可能でしたが、サイズとのバランスを考えて、この実装に落ち着きました。
//asm("
//	.code
//	.align 1
//	.global adpcm_mix1
//adpcm_mix1:
//	; %r12 = pch
//	; %r13 = mixbuf
//;//{{-----------------------------------;
//;//	pushn	%r0
//;//→* Fri Dec 22 00:19:41 JST 2006 Naoyuki Sawa
//;//→- 係数テーブルを変更し、最適化してレジスタを節約できたので、不要になりました。
//;//}}-----------------------------------;
//	xld.w	%r4,  320		; %r4  = ADPCMBUFLEN
//	ld.w	%r5,  [%r12]+		; %r5  = pcm      = pch->pcm     , %r12 = &pch->data
//	ld.h	%r6,  [%r12]+		; %r6  = data     = pch->data    , %r12 = &pch->delta
//	ld.h	%r7,  [%r12]+		; %r7  = delta    = pch->delta   , %r12 = &pch->progress
//	ld.h	%r10, [%r12]+		; %r10 = progress = pch->progress, %r12 = &pch->rate
//	ld.h	%r11, [%r12]+		; %r11 = rate     = pch->rate    , %r12 = &pch->len
//	ld.w	%r14, [%r12]+		; %r14 = len      = pch->len     , %r12 = &pch->ptr
//	ld.w	%r15, [%r12]+		; %r15 = ptr      = pch->ptr     , %r12 = &pch->loop
//adpcm_mix1_L10:				; do {
//	sub	%r10, %r11		;   progress -= rate 
//	xjrge	adpcm_mix1_L30		;   if(progress < 0) {
//	xadd	%r10, %r10, 16000	;     progress += SPEAKER_FREQUENCY
//	xcmp	%r6, 16			;     if(data < (1<<(2+2))) {
//	xjrge	adpcm_mix1_L20
//	ld.ub	%r6, [%r15]+		;       data = (*ptr++ | (1<<8)) << 2
//	xoor	%r6, %r6, 256
//	xsla	%r6, 2
//adpcm_mix1_L20:				;     }
//	xand	%r9, %r6, 12		;     %r9 = adpcm = data & (3<<2)
//	xsra	%r6, 2			;     data >>= 2
//	ext	adpcm_tbl@ah		;     %r9 = adpcm_tbl[adpcm]
//	ext	adpcm_tbl@al
//;//{{-----------------------------------;
//;//	ld.w	%r9, [%r9]		;     %r9[0:15] = adpcm_tbl[adpcm][0], %r9[31:16] = adpcm_tbl[adpcm][1]
//;//	mlt.h	%r9, %r7		;     pcm  += (delta*adpcm_tbl[adpcm][0]) >> 8
//;//	ld.w	%r0, %alr		;     ※ここでもう一つレジスタをやりくりできれば
//;//	xsra	%r0, 8			;     ※先頭のpush %r0と末尾のpop %r0を無くせるのですが…
//;//	add	%r5, %r0		;     ※いろいろ試してみたけれど、断念しました。
//;//	xsra	%r9, 16			;     %r9[0:15] = adpcm_tbl[adpcm][1]
//;//	mlt.h	%r9, %r7		;     delta = (delta*adpcm_tbl[adpcm][1]) >> 8
//;//	ld.w	%r7, %alr
//;//	xsra	%r7, 8
//;//↓* Fri Dec 22 00:19:41 JST 2006 Naoyuki Sawa
//	ld.w	%r9, [%r9]		;     %r9[0:15] = adpcm_tbl[adpcm][0], %r9[31:16] = SWAPH(adpcm_tbl[adpcm][1])
//	mlt.h	%r9, %r7		;     delta = (delta*adpcm_tbl[adpcm][0]) >> 8
//	ld.w	%r7, %alr
//	xsra	%r7, 8
//	swap	%r9, %r9		;     %r9[0:15] = adpcm_tbl[adpcm][1]
//	mlt.h	%r9, %r7		;     pcm  += (delta*adpcm_tbl[adpcm][1]) >> 8
//	ld.w	%r9, %alr
//	xsra	%r9, 8
//	add	%r5, %r9
//;//}}-----------------------------------;
//adpcm_mix1_L30:				;   }
//	ld.w	%r9, [%r13]		;   *mixbuf++ = pcm
//	add	%r9, %r5
//	ld.w	[%r13]+, %r9
//	xsub	%r14, %r14, 1		;   if(!--len) {
//	xjrne	adpcm_mix1_L50
//	xld.w	%r9, [%r12+0]		;     %r9 = loop = pch->loop - 1
//	xsub	%r9, %r9, 1
//	xjrlt	adpcm_mix1_L40		;     if(loop >= 0) {
//	xld.w	[%r12+0], %r9		;       pch->loop = loop
//	xjreq	adpcm_mix1_L60		;       if(!loop) break
//adpcm_mix1_L40:				;     }
//	xld.w	%r5,  0			;     pcm      = 0
//	xld.w	%r6,  0			;     data     = 0
//	xld.w	%r7,  16		;     delta    = 16
//	xld.w	%r10, 0			;     progress = 0
//	xld.w	%r14, [%r12+4]		;     len      = pch->init_len
//	xld.w	%r15, [%r12+8]		;     ptr      = pch->init_ptr
//adpcm_mix1_L50:				;   }
//	xsub	%r4, %r4, 1		; } while(--n)
//	xjrne	adpcm_mix1_L10
//adpcm_mix1_L60:
//	xsub	%r12, %r12, 20		;                           %r12 = &pch->pcm
//	ld.w	[%r12]+, %r5		; pch->pcm      = pcm     , %r12 = &pch->data
//	ld.h	[%r12]+, %r6		; pch->data     = data    , %r12 = &pch->delta
//	ld.h	[%r12]+, %r7		; pch->delta    = delta   , %r12 = &pch->progress
//	ld.h	[%r12]+, %r10		; pch->progress = progress, %r12 = &pch->rate
//	ld.h	[%r12]+, %r11		; pch->rate     = rate    , %r12 = &pch->len
//	ld.w	[%r12]+, %r14		; pch->len      = len     , %r12 = &pch->ptr
//	ld.w	[%r12] , %r15		; pch->ptr      = ptr
//;//{{-----------------------------------;
//;//	popn	%r0
//;//→* Fri Dec 22 00:19:41 JST 2006 Naoyuki Sawa
//;//→- 係数テーブルを変更し、最適化してレジスタを節約できたので、不要になりました。
//;//}}-----------------------------------;
//	ret
//");
//↓* Mon Jul 15 20:32:11 JST 2013 Naoyuki Sawa
//↓- 音量設定機能に対応しました。
//↓- 前回の版(2006/12/21)は速度よりも省メモリを優先していましたが、今回の版(2013/07/15)は速度を優先しました。
//↓  関数先頭と末尾でコードサイズ増加しますが、頻繁に使う定数をレジスタに保持して高速化する調整を行いました。
//↓  本関数のコードサイズは少し増えましたが、代わりに係数テーブルを高速RAM⇒SRAMへ移動したので、framadp.oのサイズは172バイトのまま変化無しです。
//↓  その代わりに、SRAMに配置されるclipadp.oのコードサイズが、184バイト増加しました。(adpcm_att()関数や音量設定機能の追加による増加を含みます。)
asm("
		.code
		.align		1
		.global		adpcm_mix1
adpcm_mix1:
		;// %r12 := pch
		;// %r13 := mixbuf
		pushn		%r3
		xld.w		%r1,  320		;// %r1  := n = ADPCMBUFLEN
		xld.w		%r2,  16000		;// %r2  := SPEAKER_FREQUENCY	(高速化のために定数を保持)
		xld.w		%r3,  adpcm_tbl		;// %r3  := adpcm_tbl		(高速化のために定数を保持)
		ld.w		%r4,  [%r12]+		;// %r4  := ptr      = pch->ptr     , %r12 := &pch->len
		ld.w		%r5,  [%r12]+		;// %r5  := len      = pch->len     , %r12 := &pch->pcm
		ld.w		%r6,  [%r12]+		;// %r6  := pcm      = pch->pcm     , %r12 := &pch->data
		ld.h		%r7,  [%r12]+		;// %r7  := data     = pch->data    , %r12 := &pch->delta
		ld.h		%r10, [%r12]+		;// %r10 := delta    = pch->delta   , %r12 := &pch->progress
		ld.h		%r11, [%r12]+		;// %r11 := progress = pch->progress, %r12 := &pch->rate
		ld.h		%r14, [%r12]+		;// %r14 := rate     = pch->rate    , %r12 := &pch->vol
		ld.h		%r15, [%r12]+		;// %r15 := vol      = pch->vol     , %r12 := &pch->loop
adpcm_mix1_L10:	;//-------------------------------------;// do {
		sub		%r11, %r14		;//   progress -= rate 
		jrge		adpcm_mix1_L30		;//   if(progress < 0) {
	;//	add		%r11, %r2		;//     progress += SPEAKER_FREQUENCY	------------------------------------------------+
		cmp		%r7, 16			;//     if(data < (1<<(2+2))) {								|
		jrge.d		adpcm_mix1_L20		;//											|
		add		%r11, %r2		;//     progress += SPEAKER_FREQUENCY	<-----------------------------------------------+	*delay*
		ld.ub		%r7, [%r4]+		;//       data := (*ptr++ | (1<<8)) << 2
		xoor		%r7, %r7, 256
		sla		%r7, 2
adpcm_mix1_L20:						;//     }
		xand		%r9, %r7, 12		;//     %r9  := adpcm = data & (3<<2)
	;//	sra		%r7, 2			;//     data >>= 2	----------------------------------------------------------------+
		add		%r9, %r3		;//     %r9  := &adpcm_tbl[adpcm]							|
		ld.w		%r9, [%r9]		;//     %r9[0:15] = adpcm_tbl[adpcm][0], %r9[31:16] = SWAPH(adpcm_tbl[adpcm][1])	|
		sra		%r7, 2			;//     data >>= 2	<---------------------------------------------------------------+	*anti-interlock*
		mlt.h		%r9, %r10		;//     %r10 := delta = (delta*adpcm_tbl[adpcm][0]) >> 8
		ld.w		%r10, %alr
		sra		%r10, 8
		swap		%r9, %r9		;//     %r9[0:15] = adpcm_tbl[adpcm][1]
		mlt.h		%r9, %r10		;//     %r6  := pcm  += (delta*adpcm_tbl[adpcm][1]) >> 8
		ld.w		%r9, %alr
		sra		%r9, 8
		add		%r6, %r9
adpcm_mix1_L30:						;//   }
		ld.w		%r9, [%r13]		;//   *mixbuf++ += (pcm * vol) >> 8
		mlt.w		%r6, %r15		;//   pcm値は(pcm>32767)及び(pcm<-32768)となり得るので、ASM版では(pcm*vol)に32bit乗算(mlt.w)を用いること。
		ld.w		%r0, %alr
;//{{2014/07/07削除:音量計算の「>>8」の処理をmix1でチャネル毎に行っていたのを、mix2で全チャネル加算後に1回で行うように最適化しました。
;//		sra		%r0, 8
;//}}2014/07/07削除:音量計算の「>>8」の処理をmix1でチャネル毎に行っていたのを、mix2で全チャネル加算後に1回で行うように最適化しました。
		add		%r9, %r0
		ld.w		[%r13]+, %r9
		;//-------------------------------------;//
		sub		%r5, 1			;//   if(!--len) {
		jrne		adpcm_mix1_L50
		ld.h		%r9, [%r12]		;//     %r9  := loop = pch->loop - 1
		sub		%r9, 1			;//												!INTERLOCK!
		jrlt		adpcm_mix1_L40		;//     if(loop >= 0) {
		ld.h		[%r12], %r9		;//       pch->loop = loop
		jreq		adpcm_mix1_L60		;//       if(!loop) break
adpcm_mix1_L40:						;//     }
		xld.w		%r4, [%r12+2]		;//     %r4  := ptr      = pch->init_ptr
		xld.w		%r5, [%r12+6]		;//     %r5  := len      = pch->init_len
		ld.w		%r6,  0			;//     %r6  := pcm      = 0
		ld.w		%r7,  0			;//     %r7  := data     = 0
		ld.w		%r10, 16		;//     %r10 := delta    = 16
		ld.w		%r11, 0			;//     %r11 := progress = 0
adpcm_mix1_L50:						;//   }
		sub		%r1, 1			;// } while(--n)
		jrne		adpcm_mix1_L10
adpcm_mix1_L60:	;//-------------------------------------;//
		sub		%r12, 22		;//                           %r12 := &pch->ptr
		ld.w		[%r12]+, %r4		;// pch->ptr      = ptr     , %r12 := &pch->len
		ld.w		[%r12]+, %r5		;// pch->len      = len     , %r12 := &pch->pcm
		ld.w		[%r12]+, %r6		;// pch->pcm      = pcm     , %r12 := &pch->data
		ld.h		[%r12]+, %r7		;// pch->data     = data    , %r12 := &pch->delta
		ld.h		[%r12]+, %r10		;// pch->delta    = delta   , %r12 := &pch->progress
		ld.h		[%r12] , %r11		;// pch->progress = progress
		popn		%r3
		ret
");
#endif /*ADPCM_ASM*/

/*--------------------------------------------------------------------------*/

#ifndef ADPCM_ASM
void
adpcm_mix2(short wbuff[/*ADPCMBUFLEN*/], int mixbuf[/*ADPCMBUFLEN*/])
{
	int n;
	int output;

	n = ADPCMBUFLEN;
	do {
//{{2014/07/07変更:音量計算の「>>8」の処理をmix1でチャネル毎に行っていたのを、mix2で全チャネル加算後に1回で行うように最適化しました。
//		output = *mixbuf++;
//↓2014/07/07変更:音量計算の「>>8」の処理をmix1でチャネル毎に行っていたのを、mix2で全チャネル加算後に1回で行うように最適化しました。
		output = *mixbuf++ >> 8;
//}}2014/07/07変更:音量計算の「>>8」の処理をmix1でチャネル毎に行っていたのを、mix2で全チャネル加算後に1回で行うように最適化しました。
		if(output > SHRT_MAX) output = SHRT_MAX;
		if(output < SHRT_MIN) output = SHRT_MIN;
		*wbuff++ = output;
	} while(--n);
}
#else /*ADPCM_ASM*/
asm("
	.code
	.align 1
	.global adpcm_mix2
adpcm_mix2:
	; %r12 = wbuff
	; %r13 = mixbuf
	xld.w %r4, 320			; ADPCMBUFLEN=320
	xld.w %r5, 32767		; SHRT_MAX= 32767
	not %r6, %r5			; SHRT_MIN=-32768
adpcm_mix2_L10:
	ld.w %r7, [%r13]+
;//{{2014/07/07追加:音量計算の「>>8」の処理をmix1でチャネル毎に行っていたのを、mix2で全チャネル加算後に1回で行うように最適化しました。
	sra %r7, 8
;//}}2014/07/07変更:音量計算の「>>8」の処理をmix1でチャネル毎に行っていたのを、mix2で全チャネル加算後に1回で行うように最適化しました。
	cmp %r7, %r5
	xjrle.d adpcm_mix2_L20
	cmp %r7, %r6			; *delay*
	ld.w %r7, %r5
adpcm_mix2_L20:
	xjrge.d adpcm_mix2_L30
	sub %r4, 1			; *delay*
	ld.w %r7, %r6
adpcm_mix2_L30:
	ld.h [%r12]+, %r7
	xjrne adpcm_mix2_L10
	ret
");
#endif /*ADPCM_ASM*/

