
#pragma once

#include "Covox.h"


#define EMU_GLOBAL_VOLUME 49152.0

class CEMU8930 : public CCovox
{
		enum
		{
			CHAN_A = 0,
			CHAN_B = 1,
			CHAN_C = 2,
			SOUND_CHANNELS = 3
		};
#pragma pack(push)
#pragma pack(1) // всё должно быть именно так, несмотря на предупреждения всяких анализаторов. Я лучше знаю.
		struct AYREGS_8910
		{
			WORD fA, fB, fC;    //0,1; 2,3; 4,5;
			BYTE noise, mix;    //6; 7;
			BYTE vA, vB, vC;    //8; 9; 10;
			WORD envT;          //11,12;
			BYTE env;           //13;
			BYTE portA, portB;  //14; 15;
		};
		struct AYREGS_8930
		{
			WORD fA, fB, fC;    //0,1; 2,3; 4,5;
			BYTE noise, mix;    //6; 7;
			BYTE vA, vB, vC;    //8; 9; 10;
			WORD envTA;         //11,12;
			BYTE envA;          //13;
			BYTE portA, portB;  //14; 15;
			WORD envTB;         //16,17;
			WORD envTC;         //18,19;
			BYTE envB;          //20;
			BYTE envC;          //21;
			BYTE dutyCycleA;    //22;
			BYTE dutyCycleB;    //23;
			BYTE dutyCycleC;    //24;
			BYTE noiseANDMask;  //25;
			BYTE noiseORMask;   //26;
			BYTE reserved13b;   //27*;
			BYTE reserved14b;   //28*;
			BYTE envA15b;       //29; - дубль, или тут значимы только 4 старших бита
			BYTE reserved16b;   //30*;
			BYTE test;          //31*; *-регистры недоступны
		};

		union
		{
			struct AYREGS_8910 r_legacy; //обозначения регистров для более удобного доступа к их значениям.
			struct AYREGS_8930 r_expand;
			BYTE reg[32]; //содержимое регистров
		};

#pragma pack(pop)

		static const int pantbl[6];
		static const int voltbl[32];
		double vols[6][32];

		int m_nClock, m_nRate, m_nBaseIncrement;
		int m_nBaseCount;

		bool m_bHQ;
		BYTE m_nSynthReg;
		bool m_bExpandedMode;
		int m_nExpandedBank;
		struct SoundChannel
		{
			int nCount;
			int nVolume; //громкость звучания канала
			int nFreq; //частота ноты канала
			bool bEnv; //флаг, что включена огибающая на канале
			bool bToneToggle;
			bool bToneMask; //флаг звучания ноты
			bool bNoiseMask; //флаг звучания шума
			int nDutyCycle; //скважность
			//всё для огибающей, тут их три на каждый канал.
			int nEnvPtr;
			int nDEnv;
			bool bEnvLValue;
			bool bEnvAttack;
			bool bEnvHold;
			bool bEnvAlt;
			int nEnvFreq;
			int nEnvCount;
		};

		struct SoundChannel m_Channel[SOUND_CHANNELS];


		DWORD m_nNoiseSeed, m_nNoiseValue;
		int m_nNoiseCount;
		int m_nNoiseFreq;
		BYTE m_nNoiseORMask, m_nNoiseANDMask;

		/* m_nRate converter*/
		int m_nRealStep;
		int m_nPSGTime;
		int m_nPSGStep;

		/* I/O Ctrl*/
		DWORD m_nAddr;

		double m_dMixL, m_dMixR;
		void calc();
		virtual void OnReset();
		virtual void NextTick();

	public:
		CEMU8930();
		virtual ~CEMU8930();
		virtual bool GetSample(double &sampleL, double &sampleR);

		void    PSG_reset();
		void    PSG_init(int c, int r);
		void    PSG_set_quality(bool q);
		void    PSG_setVolumeMode();
		void    PSG_writeReg(DWORD reg, BYTE val);
		void    PSG_writeIO(DWORD nAddr, BYTE val);
		BYTE    PSG_readReg(int reg);
		BYTE    PSG_readIO();
		void    PSG_calc(SAMPLE_TYPE &L, SAMPLE_TYPE &R);

		// Writing register address
		void synth_write_address(WORD word)
		{
			m_nSynthReg = (~word & 0xF);
		}

		// Writing data
		void synth_write_data(BYTE byte)
		{
			PSG_writeReg(m_nSynthReg, ~byte);
		}
};

