
#pragma once

#include "BKSoundDevice.h"
#include "Config.h"

constexpr auto VOL_LEVELS = 32;

class CEMU8930 : public CBKSoundDevice
{
		static const std::array<double, 9> m_dutycycle;
		int m_nChip{ 0 };

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

		union
		{
			AYREGS_8910 m_RLegacy; // обозначения регистров для более удобного доступа к их значениям.
			AYREGS_8930 m_RExpand;
			std::array<uint8_t, 32> m_reg{}; // содержимое регистров
		};

#pragma pack(pop)

		static const std::array<std::array<int, VOL_LEVELS>, 2> m_voltbl;
		std::array<double, VOL_LEVELS> m_vols;
		std::array < double, AY_PAN_BASE + 1 > m_dPanKoeff;

		int                 m_nClock{ 0 }, m_nRate{ 0 };

		uint32_t            m_nBaseIncrement{ 0 };
		uint32_t            m_nBaseCount{ 0 };

		/* m_nRate converter*/
		double              m_dRealStep{ 0.0 };
		double              m_dPSGTime{ 0.0 };
		double              m_dPSGStep{ 0.0 };

		bool                m_bHQ{ true };
		uint8_t             m_nSynthReg{0xff};
		bool                m_bExpandedMode{false};
		int                 m_nExpandedBank{ 0 };

		struct SoundChannel
		{
			int     nCount{ 0 };
			int     nVolume{ 0 };        // громкость звучания канала
			int     nFreq{ 0 };          // частота ноты канала
			bool    bEnv{ false };       // флаг, что включена огибающая на канале
			bool    bToneToggle{ false };
			bool    bToneMask{ false };  // флаг звучания ноты
			bool    bNoiseMask{ false }; // флаг звучания шума
			int     nDutyCycle{ 0 };     // скважность
			// всё для огибающей, тут их три на каждый канал.
			int     nEnvPtr{ 0 };
			int     nDEnv{ 0 };
			bool    bEnvLValue{ false };
			bool    bEnvAttack{ false };
			bool    bEnvHold{ false };
			bool    bEnvAlt{ false };
			int     nEnvFreq{ 0 };
			int     nEnvCount{ 0 };
			// для duty cycle
			std::array<double, 2> frqT{};
			int     frqT_t{ 0 };
			int     phaseT{ 0 };

			std::array<double, 2> frqE{};
			int     frqE_t{ 0 };
			int     phaseE{ 0 };

			// указатели на переменные из конфига
			double *pVolume{ nullptr };        // громкость канала
			int    *pPanL{ nullptr };          // панорамирование влево
			int    *pPanR{ nullptr };          // панорамирование вправо
		};

		std::array<SoundChannel, AY_CHANS> m_Channel;

		uint32_t            m_nNoiseSeed{ 0 }, m_nNoiseValue{ 0 };
		int                 m_nNoiseCount{ 0 };
		int                 m_nNoiseFreq{ 0 };
		uint8_t             m_nNoiseORMask{ 0 }, m_nNoiseANDMask{ 0 };

		/* I/O Ctrl*/
		uint8_t             m_nAddr{ 0 };

		double              m_dMixL{ 0.0 }, m_dMixR{ 0.0 };
		void                calc();
		void                calcDutyE(SoundChannel &ch); //расчёт частоты с учётом dutyCycle для огибающей
		void                calcDutyT(SoundChannel &ch); //расчёт частоты с учётом dutyCycle для тона
	protected:

	public:
		CEMU8930();
		void                setChip(int nChip);
		virtual ~CEMU8930() override;
		virtual void        ReInit() override;
		virtual void        Reset() override;

		virtual void        GetSample(sOneSample *pSm) override;

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

		// Writing address
		void                synth_write_address(uint8_t addr)
		{
			m_nSynthReg = addr;
		}

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

/*
Проверил работу AY8930.
Таки да. Она не абсолютно совместима с AY8910.
В AY8910 миксер не влияет на амплитуду волнового пакета, она там выставляется независимо от разрешения каналов.
А в AY8930 очень даже влияет. И если в миксере запретить тон и шум в канале, то и волновой пакет тоже не работает,
т.е. может зависит только от тона, настолько тщательно я не проверял.
В общем, различие только в способе звучания чистого волнового пакета, во всём остальном - никакой разницы,
работает 1:1, только генератор шума генерирует более приятно звучащий шум.
для AY8910 традиционно чистый волпак включался так:

movb #77,mixer
movb #20,chan_x_amp ;выставляем амплитуду канала, в котором будет звучать чистый волновой пакет
mov #period,period_vp
movb #form,form_vp

И AY8910 начинает жужжать, так, как велит ему волпак. А AY8930 при этом молчит, как партизан.
Но есть способ заставить его воспроизвести чистый волновой пакет.
Это делается так:

mov #0,chan_b_tone ;в регистре тона обязательно должен быть 0
movb #75,mixer ;включаем звучание тона, разрешая этим волпак, в канале B
movb #20,chan_b_amp ;выставляем амплитуду канала, в котором будет звучать чистый волновой пакет
mov #period,period_vp
movb #form,form_vp

И AY8930 начинает жужжать точно так же как и AY8910.
Но при этом способе уже AY8910 молчит как партизан.
Таким образом, использовать AY8930 можно. Но не вместо AY8910, а как отдельный муз.сопр., для которого надо
будет немного подправить код плееров.
*/
