/*	
 *	framsv.c
 *
 *	P/ECE SUPERVISION Video/Sound Emulator
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2001-2005 Naoyuki Sawa
 *
 *	* Thu Apr 14 05:55:00 JST 2005 Naoyuki Sawa
 *	- 作成開始。
 */
#include "clip.h"

/****************************************************************************
 *	Video
 ****************************************************************************/

void
svvideo_update(unsigned char vbuff[/*160*160*/])
{
	SVVIDEO* video = &svvideo;
	unsigned char* ram = video->ram;
	unsigned char* vram = &ram[0x4000];
	//
	int xscrl;
	int yscrl;
	int ofs;
	int x;
	int y;
	int v;

	xscrl = ram[0x2002];
	yscrl = ram[0x2003];
	ofs = (yscrl * 192 + xscrl) / 4; /* 0..12303 */
	xscrl &= 3;

	if(!xscrl) {
		/* ほとんどのゲームは、4ピクセル単位のスクロールしか使いません。(xscrl[1:0]=0)
		 * 4ピクセル単位のスクロールは、画面左右の端数ピクセルが無いため、高速に処理できます。
		 */
		y = 160;
		do {
			x = 160 / 4;
			do {
				if(ofs >= (192 * 170) / 4) { /* 0..8159 */
					ofs -= (192 * 170) / 4;
				}
				v = vram[ofs++];
				*vbuff++ = v & 3; v >>= 2;
				*vbuff++ = v & 3; v >>= 2;
				*vbuff++ = v & 3; v >>= 2;
				*vbuff++ = v;
			} while(--x);
			ofs += (192 - 160) / 4; /* Gap */
		} while(--y);
	} else {
		/* ごく一部のゲーム(SSSnake)のみ、1ピクセル単位のスクロールを使います。(xscrl[1:0]=1..3)
		 * 1ピクセル単位のスクロールは、画面左右の端数ピクセルの処理が必要で、やや遅くなります。
		 */
		y = 160;
		do {
			if(ofs >= (192 * 170) / 4) { /* 0..8159 */
				ofs -= (192 * 170) / 4;
			}
			v = vram[ofs++] >> xscrl * 2;
			x = 4 - xscrl;
			do {
				*vbuff++ = v & 3; v >>= 2;
			} while(--x);
			//
			x = 160 / 4 - 1;
			do {
				if(ofs >= (192 * 170) / 4) { /* 0..8159 */
					ofs -= (192 * 170) / 4;
				}
				v = vram[ofs++];
				*vbuff++ = v & 3; v >>= 2;
				*vbuff++ = v & 3; v >>= 2;
				*vbuff++ = v & 3; v >>= 2;
				*vbuff++ = v;
			} while(--x);
			//
			if(ofs >= (192 * 170) / 4) { /* 0..8159 */
				ofs -= (192 * 170) / 4;
			}
			v = vram[ofs/*++しちゃダメ!*/];
			x = xscrl;
			do {
				*vbuff++ = v & 3; v >>= 2;
			} while(--x);
			ofs += (192 - 160) / 4; /* Gap */
		} while(--y);
	}
}

#ifndef PIECE

/*
 * (転送元 横 5ピクセル -> 転送先 横 4ピクセル) x 32回
 * (転送元 縦20ライン   -> 転送先 縦11ライン  ) x  8回
 *
 * 20ライン -> 11ラインの圧縮パターンは、次のとおりです。
 *
 *			転送元Line#	転送先Line#
 *	i=3		-----------	-----------
 *	i=3->2		 0+ 1		 0
 *	i=2->1		 2+ 3		 1
 *	i=1->0->6	 4   		 2
 *	i=6->5		 5+ 6		 3
 *	i=5->4		 7+ 8		 4
 *	i=4->3		 9+10		 5
 *	i=3->2		11+12		 6
 *	i=2->1		13+14		 7
 *	i=1->0->6	15   		 8
 *	i=6->5		16+17		 9
 *	i=5->4		18+19		10
 *
 * [note]
 *	* framdmv.cのdmgvideo_reduce()をほとんどそのまま流用しました。
 *	  変更点は、iの初期値(1->3)と再設定値(3->6)だけです。
 */
void
svvideo_reduce(unsigned char dst[/*128*88*/], const unsigned char src[/*160*160*/])
{
	int i;
	int x;
	int y;
	int z;

	z = 8;
	do {
		y = 11;
		i = 3;
		do {
			x = 32;
			if(!--i) {
				do {
					dst[0] =  src[0];
					dst[1] =  src[1];
					dst[2] =  src[2];
					dst[3] = (src[3] + src[4] + 1) >> 1;
					dst += 4;
					src += 5;
				} while(--x);
				i = 6;
			} else {
				do {
					dst[0] = (src[0] + src[160] + 1) >> 1;
					dst[1] = (src[1] + src[161] + 1) >> 1;
					dst[2] = (src[2] + src[162] + 1) >> 1;
					dst[3] = (src[3] + src[163] +
					          src[4] + src[164] + 2) >> 2;
					dst += 4;
					src += 5;
				} while(--x);
				src += 160;
			}
		} while(--y);
	} while(--z);
}

#endif /*PIECE*/

/****************************************************************************
 *	Sound
 ****************************************************************************/

#ifndef PIECE

/*   Tone 0  符号付き5bit
 * + Tone 1  符号付き5bit
 * + Noise   符号付き5bit
 * + DMA     符号付き5bit (※未対応)
 * =         符号付き7bit
 */
#define SVSOUND_VOLUME_SHIFT	((16-7)+1/*調整可*/)

void
svsound_tone_mix(SVSOUNDTONE* tone, short wbuff[/*SVSOUNDBUFLEN*/])
{
	int i;
	int volume;
	int period;
	int polar;
	int count;
	int output;

	volume = tone->volume;
	if(!volume) {
		return;
	}

	period = tone->period;
	if(!period) {
		return;
	}

	polar = tone->polar;	/* 取り出し */
	count = tone->count;	/* 取り出し */

	output = polar ? volume : -volume;

	i = SVSOUNDBUFLEN;
	do {
		count -= (4000000 / SPEAKER_FREQUENCY) * 2/*極性反転周期は波形周期の2倍*/;
		if(count < 0) {
			do {
				polar ^= 1;
			} while((count += period) < 0);
			output = polar ? volume : -volume;
		}
		*wbuff++ += output;
	} while(--i);

	tone->polar = polar;	/* 書き戻し */
	tone->count = count;	/* 書き戻し */
}

void
svsound_noise_mix(SVSOUNDNOISE* noise, short wbuff[/*SVSOUNDBUFLEN*/])
{
	int i;
	int x;
	int volume;
	int period;
	int lfsr;
	int count;
	int output;

	if(!noise->on) {
		return;
	}

	volume = noise->volume;
	if(!volume) {
		return;
	}

	period = noise->period;
	if(!period) {
		return;
	}

	lfsr = noise->lfsr;	/* 取り出し */
	count = noise->count;	/* 取り出し */

	output = (lfsr & 1) ? volume : -volume;

	i = SVSOUNDBUFLEN;
	do {
		count--; /* f = 16KHz/period (仕様不明。独断です) */
		if(count < 0) {
			do {
				/* 9bit: tap=0,5 (仕様不明。独断です) */
				x = (lfsr ^ (lfsr >> 5)) & 1;
				lfsr = (lfsr >> 1) | (x << 8);
			} while((count += period) < 0);
			output = (lfsr & 1) ? volume : -volume;
		}
		*wbuff++ += output;
	} while(--i);

	noise->lfsr = lfsr;	/* 書き戻し */
	noise->count = count;	/* 書き戻し */
}

void
svsound_volume_shift(short wbuff[/*SVSOUNDBUFLEN*/])
{
	int i;
	int v;

	i = SVSOUNDBUFLEN;
	do {
		v = *wbuff;
		v <<= SVSOUND_VOLUME_SHIFT;
		if(v >  32767) v =  32767;
		if(v < -32768) v = -32768;
		*wbuff++ = v;
	} while(--i);
}

#endif /*PIECE*/
