text: /* 
  	SN76489 emulation
  	by Maxim in 2001 and 2002
  	converted from my original Delphi implementation
  
  	I'm a C newbie so I'm sure there are loads of stupid things
  	in here which I'll come back to some day and redo
  
  	Includes:
  	- Super-high quality tone channel "oversampling" by calculating fractional positions on transitions
  	- Noise output pattern reverse engineered from actual SMS output
  	- Volume levels taken from actual SMS output
  
  	07/08/04  Charles MacDonald
  	Modified for use with SMS Plus:
  	- Added support for multiple PSG chips.
  	- Added reset/config/update routines.
  	- Added context management routines.
  	- Removed SN76489_GetValues().
  	- Removed some unused variables.
  */
  
  #include <stdlib.h> // malloc/free
  #include <float.h> // for FLT_MIN
  #include <string.h> // for memcpy
  #include "mamedef.h"
  #include "sn76489.h"
  #include "panning.h"
  
  #define NoiseInitialState 0x8000  /* Initial state of shift register */
  #define PSG_CUTOFF        0x6     /* Value below which PSG does not output */
  
  static const int PSGVolumeValues[16] = {
  /*	// These values are taken from a real SMS2's output
  	{892,892,892,760,623,497,404,323,257,198,159,123,96,75,60,0}, // I can't remember why 892... :P some scaling I did at some point
  	// these values are true volumes for 2dB drops at each step (multiply previous by 10^-0.1)
  	1516,1205,957,760,603,479,381,303,240,191,152,120,96,76,60,0*/
  // The MAME core uses 0x2000 as maximum volume (0x1000 for bipolar output)
  	4096, 3254, 2584, 2053, 1631, 1295, 1029, 817, 649, 516, 410, 325, 258, 205, 163, 0
  };
  
  /*static SN76489_Context SN76489[MAX_SN76489];*/
  static SN76489_Context* LastChipInit = NULL;
  //static unsigned short int FNumLimit;
  
  
  SN76489_Context* SN76489_Init( int PSGClockValue, int SamplingRate)
  {
  	int i;
  	SN76489_Context* chip = (SN76489_Context*)malloc(sizeof(SN76489_Context));
  	if(chip)
  	{
  		chip->dClock=(float)(PSGClockValue & 0x7FFFFFF)/16/SamplingRate;
  		
  		SN76489_SetMute(chip, MUTE_ALLON);
  		SN76489_Config(chip, /*MUTE_ALLON,*/ FB_SEGAVDP, SRW_SEGAVDP, 1);
  		
  		for( i = 0; i <= 3; i++ )
  			centre_panning(chip->panning[i]);
  		//SN76489_Reset(chip);
  		
  		if ((PSGClockValue & 0x80000000) && LastChipInit != NULL)
  		{
  			// Activate special NeoGeoPocket Mode
  			LastChipInit->NgpFlags = 0x80 | 0x00;
  			chip->NgpFlags = 0x80 | 0x01;
  			chip->NgpChip2 = LastChipInit;
  			LastChipInit->NgpChip2 = chip;
  			LastChipInit = NULL;
  		}
  		else
  		{
  			chip->NgpFlags = 0x00;
  			chip->NgpChip2 = NULL;
  			LastChipInit = chip;
  		}
  	}
  	return chip;
  }
  
  void SN76489_Reset(SN76489_Context* chip)
  {
  	int i;
  
  	chip->PSGStereo = 0xFF;
  
  	for( i = 0; i <= 3; i++ )
  	{
  		/* Initialise PSG state */
  		chip->Registers[2*i] = 1;		 /* tone freq=1 */
  		chip->Registers[2*i+1] = 0xf;	 /* vol=off */
  		chip->NoiseFreq = 0x10;
  
  		/* Set counters to 0 */
  		chip->ToneFreqVals[i] = 0;
  
  		/* Set flip-flops to 1 */
  		chip->ToneFreqPos[i] = 1;
  
  		/* Set intermediate positions to do-not-use value */
  		chip->IntermediatePos[i] = FLT_MIN;
  
  		/* Set panning to centre */
  		//centre_panning( chip->panning[i] );
  	}
  
  	chip->LatchedRegister = 0;
  
  	/* Initialise noise generator */
  	chip->NoiseShiftRegister = NoiseInitialState;
  
  	/* Zero clock */
  	chip->Clock = 0;
  }
  
  void SN76489_Shutdown(SN76489_Context* chip)
  {
  	free(chip);
  }
  
  void SN76489_Config(SN76489_Context* chip, /*int mute,*/ int feedback, int sr_width, int boost_noise)
  {
  	//chip->Mute = mute;
  	chip->WhiteNoiseFeedback = feedback;
  	chip->SRWidth = sr_width;
  }
  
  /*
  void SN76489_SetContext(int which, uint8 *data)
  {
  	memcpy( &SN76489[which], data, sizeof(SN76489_Context) );
  }
  
  void SN76489_GetContext(int which, uint8 *data)
  {
  	memcpy( data, &SN76489[which], sizeof(SN76489_Context) );
  }
  
  uint8 *SN76489_GetContextPtr(int which)
  {
  	return (uint8 *)&SN76489[which];
  }
  
  int SN76489_GetContextSize(void)
  {
  	return sizeof(SN76489_Context);
  }
  */
  void SN76489_Write(SN76489_Context* chip, int data)
  {
  	if ( data & 0x80 )
  	{
  		/* Latch/data byte  %1 cc t dddd */
  		chip->LatchedRegister = ( data >> 4 ) & 0x07;
  		chip->Registers[chip->LatchedRegister] =
  			( chip->Registers[chip->LatchedRegister] & 0x3f0 ) /* zero low 4 bits */
  			| ( data & 0xf );                            /* and replace with data */
  	} else {
  		/* Data byte        %0 - dddddd */
  		if ( !( chip->LatchedRegister % 2 ) && ( chip->LatchedRegister < 5 ) )
  			/* Tone register */
  			chip->Registers[chip->LatchedRegister] =
  				( chip->Registers[chip->LatchedRegister] & 0x00f) /* zero high 6 bits */
  				| ( ( data & 0x3f ) << 4 );                 /* and replace with data */
  		else
  			/* Other register */
  			chip->Registers[chip->LatchedRegister]=data&0x0f; /* Replace with data */
  	}
  	switch (chip->LatchedRegister) {
  	case 0:
  	case 2:
  	case 4: /* Tone channels */
  		if ( chip->Registers[chip->LatchedRegister] == 0 )
  			chip->Registers[chip->LatchedRegister] = 1; /* Zero frequency changed to 1 to avoid div/0 */
  		break;
  	case 6: /* Noise */
  		chip->NoiseShiftRegister = NoiseInitialState;        /* reset shift register */
  		chip->NoiseFreq = 0x10 << ( chip->Registers[6] & 0x3 ); /* set noise signal generator frequency */
  		break;
  	}
  }
  
  void SN76489_GGStereoWrite(SN76489_Context* chip, int data)
  {
  	chip->PSGStereo=data;
  }
  
  //void SN76489_Update(SN76489_Context* chip, INT16 **buffer, int length)
  void SN76489_Update(SN76489_Context* chip, INT32 **buffer, int length)
  {
  	int i, j;
  	int NGPMode;
  	SN76489_Context* chip2;
  	SN76489_Context* chip_t;
  	SN76489_Context* chip_n;
  
  	NGPMode = (chip->NgpFlags >> 7) & 0x01;
  	if (! NGPMode)
  	{
  		chip2 = NULL;
  		chip_t = chip_n = chip;
  	}
  	else
  	{
  		chip2 = (SN76489_Context*)chip->NgpChip2;
  		if (! (chip->NgpFlags & 0x01))
  		{
  			chip_t = chip;
  			chip_n = chip2;
  		}
  		else
  		{
  			chip_t = chip2;
  			chip_n = chip;
  		}
  	}
  	
  	for( j = 0; j < length; j++ )
  	{
  		/* Tone channels */
  		for ( i = 0; i <= 2; ++i )
  			if ( (chip_t->Mute >> i) & 1 )
  			{
  				if ( chip_t->IntermediatePos[i] != FLT_MIN )
  					/* Intermediate position (antialiasing) */
  					chip->Channels[i] = (short)( PSGVolumeValues[chip->Registers[2 * i + 1]] * chip_t->IntermediatePos[i] );
  				else
  					/* Flat (no antialiasing needed) */
  					chip->Channels[i]= PSGVolumeValues[chip->Registers[2 * i + 1]] * chip_t->ToneFreqPos[i];
  			}
  			else
  				/* Muted channel */
  				chip->Channels[i] = 0;
  
  		/* Noise channel */
  		if ( (chip_t->Mute >> 3) & 1 )
  		{
  			//chip->Channels[3] = PSGVolumeValues[chip->Registers[7]] * ( chip_n->NoiseShiftRegister & 0x1 ) * 2; /* double noise volume */
  			// Now the noise is bipolar, too. -Valley Bell
  			chip->Channels[3] = PSGVolumeValues[chip->Registers[7]] * (( chip_n->NoiseShiftRegister & 0x1 ) * 2 - 1);
  			// due to the way the white noise works here, it seems twice as loud as it should be
  			if (chip->Registers[6] & 0x4 )
  				chip->Channels[3] >>= 1;
  		}
  		else
  			chip->Channels[i] = 0;
  
  		// Build stereo result into buffer
  		buffer[0][j] = 0;
  		buffer[1][j] = 0;
  		if (! chip->NgpFlags)
  		{
  			// For all 4 channels
  			for ( i = 0; i <= 3; ++i )
  			{
  				if ( ( ( chip->PSGStereo >> i ) & 0x11 ) == 0x11 )
  				{
  					// no GG stereo for this channel
  					if ( chip->panning[i][0] == 1.0f )
  					{
  						buffer[0][j] += chip->Channels[i]; // left
  						buffer[1][j] += chip->Channels[i]; // right
  					}
  					else
  					{
  						buffer[0][j] += (INT32)( chip->panning[i][0] * chip->Channels[i] ); // left
  						buffer[1][j] += (INT32)( chip->panning[i][1] * chip->Channels[i] ); // right
  					}
  				}
  				else
  				{
  					// GG stereo overrides panning
  					buffer[0][j] += ( chip->PSGStereo >> (i+4) & 0x1 ) * chip->Channels[i]; // left
  					buffer[1][j] += ( chip->PSGStereo >>  i    & 0x1 ) * chip->Channels[i]; // right
  				}
  			}
  		}
  		else
  		{
  			if (! (chip->NgpFlags & 0x01))
  			{
  				// For all 3 tone channels
  				for (i = 0; i < 3; i ++)
  				{
  					buffer[0][j] += (chip->PSGStereo >> (i+4) & 0x1 ) * chip ->Channels[i]; // left
  					buffer[1][j] += (chip->PSGStereo >>  i    & 0x1 ) * chip2->Channels[i]; // right
  				}
  			}
  			else
  			{
  				// noise channel
  				i = 3;
  				buffer[0][j] += (chip->PSGStereo >> (i+4) & 0x1 ) * chip2->Channels[i]; // left
  				buffer[1][j] += (chip->PSGStereo >>  i    & 0x1 ) * chip ->Channels[i]; // right
  			}
  		}
  
  		/* Increment clock by 1 sample length */
  		chip->Clock += chip->dClock;
  		chip->NumClocksForSample = (int)chip->Clock;  /* truncate */
  		chip->Clock -= chip->NumClocksForSample;      /* remove integer part */
  	
  		/* Decrement tone channel counters */
  		for ( i = 0; i <= 2; ++i )
  			chip->ToneFreqVals[i] -= chip->NumClocksForSample;
  	 
  		/* Noise channel: match to tone2 or decrement its counter */
  		if ( chip->NoiseFreq == 0x80 )
  			chip->ToneFreqVals[3] = chip->ToneFreqVals[2];
  		else
  			chip->ToneFreqVals[3] -= chip->NumClocksForSample;
  	
  		/* Tone channels: */
  		for ( i = 0; i <= 2; ++i ) {
  			if ( chip->ToneFreqVals[i] <= 0 ) {   /* If the counter gets below 0... */
  				if (chip->Registers[i*2]>=PSG_CUTOFF) {
  					/* For tone-generating values, calculate how much of the sample is + and how much is - */
  					/* This is optimised into an even more confusing state than it was in the first place... */
  					chip->IntermediatePos[i] = ( chip->NumClocksForSample - chip->Clock + 2 * chip->ToneFreqVals[i] ) * chip->ToneFreqPos[i] / ( chip->NumClocksForSample + chip->Clock );
  					/* Flip the flip-flop */
  					chip->ToneFreqPos[i] = -chip->ToneFreqPos[i];
  				} else {
  					/* stuck value */
  					chip->ToneFreqPos[i] = 1;
  					chip->IntermediatePos[i] = FLT_MIN;
  				}
  				chip->ToneFreqVals[i] += chip->Registers[i*2] * ( chip->NumClocksForSample / chip->Registers[i*2] + 1 );
  			}
  			else
  				/* signal no antialiasing needed */
  				chip->IntermediatePos[i] = FLT_MIN;
  		}
  	
  		/* Noise channel */
  		if ( chip->ToneFreqVals[3] <= 0 ) {
  			/* If the counter gets below 0... */
  			/* Flip the flip-flop */
  			chip->ToneFreqPos[3] = -chip->ToneFreqPos[3];
  			if (chip->NoiseFreq != 0x80)
  				/* If not matching tone2, decrement counter */
  				chip->ToneFreqVals[3] += chip->NoiseFreq * ( chip->NumClocksForSample / chip->NoiseFreq + 1 );
  			if (chip->ToneFreqPos[3] == 1) {
  				/* On the positive edge of the square wave (only once per cycle) */
  				int Feedback;
  				if ( chip->Registers[6] & 0x4 ) {
  					/* White noise */
  					/* Calculate parity of fed-back bits for feedback */
  					switch (chip->WhiteNoiseFeedback) {
  						/* Do some optimised calculations for common (known) feedback values */
  					case 0x0003: /* SC-3000, BBC %00000011 */
  					case 0x0009: /* SMS, GG, MD  %00001001 */
  						/* If two bits fed back, I can do Feedback=(nsr & fb) && (nsr & fb ^ fb) */
  						/* since that's (one or more bits set) && (not all bits set) */
  						Feedback = ( ( chip->NoiseShiftRegister & chip->WhiteNoiseFeedback )
  							&& ( (chip->NoiseShiftRegister & chip->WhiteNoiseFeedback ) ^ chip->WhiteNoiseFeedback ) );
  						break;
  					default:
  						/* Default handler for all other feedback values */
  						/* XOR fold bits into the final bit */
  						Feedback = chip->NoiseShiftRegister & chip->WhiteNoiseFeedback;
  						Feedback ^= Feedback >> 8;
  						Feedback ^= Feedback >> 4;
  						Feedback ^= Feedback >> 2;
  						Feedback ^= Feedback >> 1;
  						Feedback &= 1;
  						break;
  					}
  				} else	  /* Periodic noise */
  					Feedback=chip->NoiseShiftRegister&1;
  
  				chip->NoiseShiftRegister=(chip->NoiseShiftRegister>>1) | (Feedback << (chip->SRWidth-1));
  			}
  		}
  	}
  }
  
  /*void SN76489_UpdateOne(SN76489_Context* chip, int *l, int *r)
  {
    INT16 tl,tr;
    INT16 *buff[2] = { &tl, &tr };
    SN76489_Update( chip, buff, 1 );
    *l = tl;
    *r = tr;
  }*/
  
  
  /*int  SN76489_GetMute(SN76489_Context* chip)
  {
    return chip->Mute;
  }*/
  
  void SN76489_SetMute(SN76489_Context* chip, int val)
  {
    chip->Mute=val;
  }
  
  void SN76489_SetPanning(SN76489_Context* chip, int ch0, int ch1, int ch2, int ch3)
  {
  	calc_panning( chip->panning[0], ch0 );
  	calc_panning( chip->panning[1], ch1 );
  	calc_panning( chip->panning[2], ch2 );
  	calc_panning( chip->panning[3], ch3 );
  }
  