/*
 *  Copyright (C) 2002-2015  The DOSBox Team
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 *  dbopl.h - Ported to SDLPAL by Lou Yihua <louyihua@21cn.com>, 2015-08-03.
 */

//#include "adlib.h"
//#include "dosbox.h"

/*
define Bits, Bitu, Bit32s, Bit32u, Bit16s, Bit16u, Bit8s, Bit8u here
*/

#if defined(_MSC_VER) && _MSC_VER <= 1600
#include <windows.h>
#define uintptr_t ULONG_PTR
#define intptr_t LONG_PTR
#define uint32_t DWORD
#define int32_t INT
#define uint16_t WORD
#define int16_t SHORT
#define uint8_t BYTE
#define int8_t CHAR
#else
#include <stdint.h>
#endif

typedef uintptr_t	Bitu;
typedef intptr_t	Bits;
typedef uint32_t	Bit32u;
typedef int32_t		Bit32s;
typedef uint16_t	Bit16u;
typedef int16_t		Bit16s;
typedef uint8_t		Bit8u;
typedef int8_t		Bit8s;

/*
define attribution that inlines/forces inlining of a function (optional)
*/
#define OPL_INLINE inline

#define GCC_UNLIKELY(x) (x)

//Use 8 handlers based on a small logatirmic wavetabe and an exponential table for volume
#define WAVE_HANDLER	10
//Use a logarithmic wavetable with an exponential table for volume
#define WAVE_TABLELOG	11
//Use a linear wavetable with a multiply table for volume
#define WAVE_TABLEMUL	12

//Select the type of wave generator routine
#define DBOPL_WAVE WAVE_TABLEMUL

namespace DBOPL {

	struct Chip;
	struct Operator;
	struct Channel;

#if (DBOPL_WAVE == WAVE_HANDLER)
	typedef Bits(DB_FASTCALL *WaveHandler) (Bitu i, Bitu volume);
#endif

	typedef Bits(DBOPL::Operator::*VolumeHandler) ();
	typedef Channel* (DBOPL::Channel::*SynthHandler) (Chip* chip, Bit32u samples, Bit32s* output);

	//Different synth modes that can generate blocks of data
	typedef enum {
		sm2AM,
		sm2FM,
		sm3AM,
		sm3FM,
		sm4Start,
		sm3FMFM,
		sm3AMFM,
		sm3FMAM,
		sm3AMAM,
		sm6Start,
		sm2Percussion,
		sm3Percussion,
	} SynthMode;

	//Shifts for the values contained in chandata variable
	enum {
		SHIFT_KSLBASE = 16,
		SHIFT_KEYCODE = 24,
	};

	struct Operator {
	public:
		//Masks for operator 20 values
		enum {
			MASK_KSR = 0x10,
			MASK_SUSTAIN = 0x20,
			MASK_VIBRATO = 0x40,
			MASK_TREMOLO = 0x80,
		};

		typedef enum {
			OFF,
			RELEASE,
			SUSTAIN,
			DECAY,
			ATTACK,
		} State;

		VolumeHandler volHandler;

#if (DBOPL_WAVE == WAVE_HANDLER)
		WaveHandler waveHandler;	//Routine that generate a wave 
#else
		Bit16s* waveBase;
		Bit32u waveMask;
		Bit32u waveStart;
#endif
		Bit32u waveIndex;			//WAVE_BITS shifted counter of the frequency index
		Bit32u waveAdd;				//The base frequency without vibrato
		Bit32u waveCurrent;			//waveAdd + vibratao

		Bit32u chanData;			//Frequency/octave and derived data coming from whatever channel controls this
		Bit32u freqMul;				//Scale channel frequency with this, TODO maybe remove?
		Bit32u vibrato;				//Scaled up vibrato strength
		Bit32s sustainLevel;		//When stopping at sustain level stop here
		Bit32s totalLevel;			//totalLevel is added to every generated volume
		Bit32u currentLevel;		//totalLevel + tremolo
		Bit32s volume;				//The currently active volume

		Bit32u attackAdd;			//Timers for the different states of the envelope
		Bit32u decayAdd;
		Bit32u releaseAdd;
		Bit32u rateIndex;			//Current position of the evenlope

		Bit8u rateZero;				//Bits for the different states of the envelope having no changes
		Bit8u keyOn;				//Bitmask of different values that can generate keyon
		//Registers, also used to check for changes
		Bit8u reg20, reg40, reg60, reg80, regE0;
		//Active part of the envelope we're in
		Bit8u state;
		//0xff when tremolo is enabled
		Bit8u tremoloMask;
		//Strength of the vibrato
		Bit8u vibStrength;
		//Keep track of the calculated KSR so we can check for changes
		Bit8u ksr;
	private:
		void SetState(Bit8u s);
		void UpdateAttack(const Chip* chip);
		void UpdateRelease(const Chip* chip);
		void UpdateDecay(const Chip* chip);
	public:
		void UpdateAttenuation();
		void UpdateRates(const Chip* chip);
		void UpdateFrequency();

		void Write20(const Chip* chip, Bit8u val);
		void Write40(const Chip* chip, Bit8u val);
		void Write60(const Chip* chip, Bit8u val);
		void Write80(const Chip* chip, Bit8u val);
		void WriteE0(const Chip* chip, Bit8u val);

		bool Silent() const;
		void Prepare(const Chip* chip);

		void KeyOn(Bit8u mask);
		void KeyOff(Bit8u mask);

		template< State state>
		Bits TemplateVolume();

		Bit32s RateForward(Bit32u add);
		Bitu ForwardWave();
		Bitu ForwardVolume();

		Bits GetSample(Bits modulation);
		Bits GetWave(Bitu index, Bitu vol);
	public:
		Operator();
	};

	struct Channel {
		Operator op[2];
		inline Operator* Op(Bitu index) {
			return &((this + (index >> 1))->op[index & 1]);
		}
		SynthHandler synthHandler;
		Bit32u chanData;		//Frequency/octave and derived values
		Bit32s old[2];			//Old data for feedback

		Bit8u feedback;			//Feedback shift
		Bit8u regB0;			//Register values to check for changes
		Bit8u regC0;
		//This should correspond with reg104, bit 6 indicates a Percussion channel, bit 7 indicates a silent channel
		Bit8u fourMask;
		Bit8s maskLeft;		//Sign extended values for both channel's panning
		Bit8s maskRight;

		//Forward the channel data to the operators of the channel
		void SetChanData(const Chip* chip, Bit32u data);
		//Change in the chandata, check for new values and if we have to forward to operators
		void UpdateFrequency(const Chip* chip, Bit8u fourOp);
		void WriteA0(const Chip* chip, Bit8u val);
		void WriteB0(const Chip* chip, Bit8u val);
		void WriteC0(const Chip* chip, Bit8u val);
		void ResetC0(const Chip* chip);

		//call this for the first channel
		template< bool opl3Mode >
		void GeneratePercussion(Chip* chip, Bit32s* output);

		//Generate blocks of data in specific modes
		template<SynthMode mode>
		Channel* BlockTemplate(Chip* chip, Bit32u samples, Bit32s* output);
		Channel();
	};

	struct Chip {
		//This is used as the base counter for vibrato and tremolo
		Bit32u lfoCounter;
		Bit32u lfoAdd;


		Bit32u noiseCounter;
		Bit32u noiseAdd;
		Bit32u noiseValue;

		//Frequency scales for the different multiplications
		Bit32u freqMul[16];
		//Rates for decay and release for rate of this chip
		Bit32u linearRates[76];
		//Best match attack rates for the rate of this chip
		Bit32u attackRates[76];

		//18 channels with 2 operators each
		Channel chan[18];

		Bit8u reg104;
		Bit8u reg08;
		Bit8u reg04;
		Bit8u regBD;
		Bit8u vibratoIndex;
		Bit8u tremoloIndex;
		Bit8s vibratoSign;
		Bit8u vibratoShift;
		Bit8u tremoloValue;
		Bit8u vibratoStrength;
		Bit8u tremoloStrength;
		//Mask for allowed wave forms
		Bit8u waveFormMask;
		//0 or -1 when enabled
		Bit8s opl3Active;

		//Return the maximum amount of samples before and LFO change
		Bit32u ForwardLFO(Bit32u samples);
		Bit32u ForwardNoise();

		void WriteBD(Bit8u val);
		void WriteReg(Bit32u reg, Bit8u val);

		Bit32u WriteAddr(Bit32u port, Bit8u val);

		void GenerateBlock2(Bitu samples, Bit32s* output);
		void GenerateBlock3(Bitu samples, Bit32s* output);

		//void Generate( Bit32u samples );
		void Setup(Bit32u r);

		Chip();
	};

	bool InitTables(void);

};		//Namespace