
#include "../interface.h"
#include <time.h>

const int SN76489::PSGVolume[16] = {
	64, 50, 39, 31, 24, 19, 15, 12, 9, 7, 5, 4, 3, 2, 1, 0
};

SN76489::SN76489(_System* _sys, DSound& dsound)
: _sys(_sys), dsound(dsound), buf()
{
	synth = new Blip_Synth<blip_high_quality, 64 * 2>();
	synth->output( &buf );
	synth->volume(1.0 * ( 0.85 / 8 ));
	synth->treble_eq( -8.0 );

	freq[0] = &registers[0];
	freq[1] = &registers[1];
	freq[2] = &registers[2];
	freq[3] = &registers[3];

	vol[0] = &registers[4];
	vol[1] = &registers[5];
	vol[2] = &registers[6];
	vol[3] = &registers[7];
}

void SN76489::enter() {
	blip_sample_t sample [1];

	while (true) { //need 48 Master Cycles for a complete calculation
		runTone(0);
		runTone(1);
		runTone(2);
		runNoise(3);

		buf.end_frame( 16 );

		if (buf.read_samples( sample, 1) == 1) {
#ifdef SAMPLE_COUNTER
			sampleCounter();
#endif
			dsound.update_sound(sample[0]);
		}
		add_cycles( 16 );
	}
}

void SN76489::sampleCounter() {
	samples_count++;

	time(&curr_t);
	if(curr_t != prev_t) {
		samples_executed = samples_count;
		samples_count = 0;
	}
	prev_t = curr_t;
}

void SN76489::runTone(u8 i) {
	int curamp = phase[i] ? *vol[i] : -(*vol[i]);

	if (--counter[i] <= 0) {
		counter[i] = *freq[i];
		if (*freq[i] > 1) {
			phase[i] ^= 1;
			synth->offset_inline( 16, -(curamp * 2) );
		}
	}
}

void SN76489::runNoise(u8 i) {
	int curamp = (NoiseShiftRegister & 0x1) * *vol[i];

	int _NoiseFreq = NoiseFreq;
	if (_NoiseFreq == 0x80) {
		_NoiseFreq = *freq[2];
		counter[i] = counter[2];
	}

	if (--counter[i] <= 0) {
		counter[i] = _NoiseFreq;
		phase[i] ^= 1;
		if ( phase[i] ) {
			int Feedback = NoiseShiftRegister;
			if ( registers[3] & 0x4 ) { //white noise
				Feedback = ((Feedback & FB_SEGAVDP) && ((Feedback & FB_SEGAVDP) ^ FB_SEGAVDP));
			} else { //periodic noise
				Feedback &= 1;
			}
			NoiseShiftRegister = (NoiseShiftRegister >> 1) | (Feedback << (SRW_SEGAVDP - 1));
			int newamp = (NoiseShiftRegister & 0x1) * *vol[i];
			synth->offset_inline( 16, newamp - curamp );
		}
	}
}

void SN76489::add_cycles(u8 cycles) {
	_sys->addclocks_apu( cycles );
	_sys->sync_apucpu();
}

void SN76489::initBlip() {
	if ( NULL != buf.set_sample_rate( 44100 ) ) {}
	buf.clock_rate( _sys->_isNTSC() ? 3579545 : 3546893 );
	buf.bass_freq( 180 );
	buf.clear();
}

void SN76489::power() {
	latched = 0;
	NoiseShiftRegister = NoiseInitialState;
	NoiseFreq =  0x10;
	samples_executed = samples_count = 0;
	
	for(u8 i = 0; i <= 3; i++) {
		registers[i] = 0;
		registers[4 + i] = 0;
		
		counter[i] = 0;
		phase[i] = 1;
	}
	initBlip();
}

void SN76489::write(u8 data) {
	if (data & 0x80) {
		latched = (data >> 4) & 0x07;
	}

	switch (latched) {
		case 0:
		case 2:
		case 4: /* tone period */
			if (data & 0x80) {
				registers[latched >> 1] = (registers[latched >> 1] & 0x3f0) | (data & 0xf);
			} else {
				registers[latched >> 1] = (registers[latched >> 1] & 0x00f) | ((data & 0x3f) << 4);
			}
			break;
		case 1:
		case 3:
		case 5:
			registers[ 4 + (latched >> 1) ] = PSGVolume[ data & 0x0f ];
			break;
		case 7: /* vol */
			registers[ 4 + (latched >> 1) ] = PSGVolume[ data & 0x0f ] << 0;
			break;
		case 6: /* noise period */
			registers[3] = data & 0x0f;
			NoiseFreq = 0x10 << ( data & 0x3); 
			NoiseShiftRegister = NoiseInitialState;
			break;
	}
}